Problem with JSON strings
mapcentia opened this issue · comments
If I use a JSON string where a value has an escaped \n
the output will be $$ quoted and the following key/value will be transformed from "a": false
to "a"$1
if the value is NOT a string:
update test set json = '{"b":"a\\n","a":false}' where id=1
set "json" = $${"b":"a\\n","a"$1}$$ DETAIL: Token "$" is invalid
I figured out where is an option that can prevent this behavior StatementFactory(PDOCompatible: true)
, but this will not be enforced unless, there are actual parameters in the statement https://github.com/sad-spirit/pg-builder/blob/2.x/src/sad_spirit/pg_builder/StatementFactory.php#L192
Please confirm whether I correctly understand what happens here: you use pg_builder to generate a query that does not contain placeholders and later feed it to PDO::prepare()
and try to PDOStatement::execute()
which fails due to the above broken query?
I figured out where is an option that can prevent this behavior
StatementFactory(PDOCompatible: true)
, but this will not be enforced unless, there are actual parameters in the statement
You are correct. It isn't enforced because that flag controls two changes:
- Generation of dollar-quoted strings
- Doubling of
?
characters in operator names (as you most probably know, lots of Postgres JSON operators have these)
While the first change is safe whether you use the built query with PDO::prepare()
/ PDOStatement::execute()
or with PDO::query()
/ PDO::exec()
, the second one is not. Thus the method tries to be smart and only adds these changes when the query has parameters and definitely will be fed to prepare()
.
That's correct.
I guess that :false in {"b":"a\\n","a":false}
is converted by PDO to a native placeholder $1 ?
But that seems only to happen when using $$ quoted strings.
I guess that :false in
{"b":"a\\n","a":false}
is converted by PDO to a native placeholder $1 ?
Exactly.
But that seems only to happen when using $$ quoted strings.
PDO cannot parse these, but it properly parses strings in single quotes. That's the reason why dollar-quoted strings aren't generated in PDO::prepare()
compatibility mode.
I thought a bit about possible fixes, the best idea so far is the following: add a $forcePDOPrepareCompatibility
bool parameter to StatementFactory::createFromAST()
. It will default to false
, if set to true
it will override both the constructor parameter and the check for placeholders, generating the queries with
- Named parameters left intact,
- No dollar-quoted strings,
?
character in operators replaced with??
.
So if you intend to use the query without placeholders in PDO::prepare()
/ PDOStatement::execute()
you'll have to explicitly set this parameter to true
.
How does that sound?
That's sound good. Thanks for taking the time to look into this issue
Released version 2.3.1 with this fix