Suggestion: PG::Connection#complile#726
Conversation
2cd9444 to
b0a2666
Compare
b0a2666 to
6b746e9
Compare
|
That sounds like a great idea! It's because I manually inserted too many parameters into too many queries from rails debug log in order to execute it in |
.. to replace code specific to the classes derived from PG::TypeMap. Also: - Deny binary format in SQL text - Make BinaryString working
|
I added a commit to use generic PG::TypeMap methods only and regarding binary data and binary format. The method name "compile" is not very specific. Is it used in any other database driver? How sounds |
|
@larskanis thank you. |
I don't have any strong preferences regarding this. |
This adds type casts like "::bytea" to the escaped and embedded values. To make that possible the OID => type name mapping is needed. It can be given as explicit CoderMapsBundle, is retrieved from BasicTypeMap* or retrieved from the database. Also allow `type_map` to be provided like `exec_params` allows. This patch changes `compile` to `embed_params` to be more specific. This patch changes the tests to compare `exec(embed_params())` to `exec_params()`. That ensures the processing by the database server is the same. Since binary format encoders are not usable for embedding into SQL text, this patch changes boolean encoders to text. There's no value in using a binary encoder for query parameters. It's not measurable faster than text for boolean type.
…rings This issue was raised by a rails test: test/cases/adapters/postgresql/bytea_test.rb:114
|
🎉 With the latest change the whole activerecord test suite runs through with only 2 expected errors, when I change the following AR lines to got through |
|
@larskanis thank you for your effort and covering cases I was not aware of. |
| # Compiles your prepared SQL statement and the given positional arguments into plain SQL string. | ||
| # | ||
| # The resulting SQL string can be used with +conn.exec+ like the prepared SQL statement and parameters with +conn.exec_params+. | ||
| # +conn.exec_params+ is usually preferred because it's faster and safer. |
There was a problem hiding this comment.
IME, these kinds of not-quite-parsers are vulnerable to SQL injection.
Perhaps a stronger or more specific statement about how this should/not be used with user input.
There was a problem hiding this comment.
I agree. If there is any mistake in regexp - the result may end up containing sql injection. For example
embed_params("select '$1' as one, $1", [%{', 2 as two, }])may result in
[{"?column?" => "', 2 as two, ", "two" => "2", "one" => ""}]instead this
[{"one" => "$1", "?column?" => "', 2 as two, "}]if we forget to cover regular string literal(e.g. 'my str') in PLACEHOLDER_RE. Thus, as long as we cover all possible variations of string literals and escape the value - we are safe. Fortunately I covered all possible scenarios in tests. As an additional measure - I can put here a guard that fails on new postgresql versions and a dev should check if any new string literals appear in a new postgresql version and adjust the implementation accordingly. Something like this(in #embed_params_and_check):
# Query returns a string like "18.4 (Ubuntu 18.4-0ubuntu0.26.04.1)"
current_pg_version = conn.exec('SHOW server_version').first['server_version'].split(' ').first
current_pg_version = Gem::Version.new(current_pg_version)
expect(current_pg_version).to be < Gem::Version.new('19'), "PostgreSQL 19 is not tested. Please ensure there are no new forms of a string literal and increase the version to make this test pass."What do you think, guys?
Introduce
PG::Connection#complilethat take sql string and position parameters and compiles it into a plain sql. I couldn't find another way how to do it using built-in tools. The motivation behind this implementation:The downside of this implementation is that it can't reliably encode binary data into a string representation except some common cases like with booleans, but do we need them at all?