Skip to content

Commit

Permalink
Use GENERIC_PLAN for Postgres 16
Browse files Browse the repository at this point in the history
  • Loading branch information
ankane committed Jul 7, 2023
1 parent 828307d commit 662d316
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 6 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.5.2 (unreleased)

- Use `GENERIC_PLAN` for Postgres 16

## 0.5.1 (2023-05-27)

- Fixed `JSON::NestingError`
Expand Down
20 changes: 14 additions & 6 deletions lib/dexter/indexer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -521,8 +521,8 @@ def conn
raise Dexter::Abort, e.message
end

def execute(query, pretty: true, params: [])
# use exec_params instead of exec for security
def execute(query, pretty: true, params: [], use_exec: false)
# use exec_params instead of exec when possible for security
#
# Unlike PQexec, PQexecParams allows at most one SQL command in the given string.
# (There can be semicolons in it, but not more than one nonempty command.)
Expand All @@ -533,7 +533,11 @@ def execute(query, pretty: true, params: [])
log colorize("[sql] #{query}#{params.any? ? " /*#{params.to_json}*/" : ""}", :cyan) if @log_sql

@mutex.synchronize do
conn.exec_params("#{query} /*dexter*/", params).to_a
if use_exec
conn.exec("#{query} /*dexter*/").to_a
else
conn.exec_params("#{query} /*dexter*/", params).to_a
end
end
end

Expand All @@ -543,7 +547,9 @@ def plan(query)

# try to EXPLAIN normalized queries
# https://dev.to/yugabyte/explain-from-pgstatstatements-normalized-queries-how-to-always-get-the-generic-plan-in--5cfi
explain_normalized = query.include?("$1")
normalized = query.include?("$1")
generic_plan = normalized && server_version_num >= 160000
explain_normalized = normalized && !generic_plan
if explain_normalized
prepared_name = "dexter_prepared"
execute("PREPARE #{prepared_name} AS #{safe_statement(query)}", pretty: false)
Expand All @@ -566,12 +572,14 @@ def plan(query)
end
end

explain_prefix = generic_plan ? "GENERIC_PLAN, " : ""

# strip semi-colons as another measure of defense
plan = JSON.parse(execute("EXPLAIN (FORMAT JSON) #{safe_statement(query)}", pretty: false).first["QUERY PLAN"], max_nesting: 1000).first["Plan"]
plan = JSON.parse(execute("EXPLAIN (#{explain_prefix}FORMAT JSON) #{safe_statement(query)}", pretty: false, use_exec: generic_plan).first["QUERY PLAN"], max_nesting: 1000).first["Plan"]

if @log_explain
# Pass format to prevent ANALYZE
puts execute("EXPLAIN (FORMAT TEXT) #{safe_statement(query)}", pretty: false).map { |r| r["QUERY PLAN"] }.join("\n")
puts execute("EXPLAIN (#{explain_prefix}FORMAT TEXT) #{safe_statement(query)}", pretty: false, use_exec: generic_plan).map { |r| r["QUERY PLAN"] }.join("\n")
end

plan
Expand Down

0 comments on commit 662d316

Please sign in to comment.