Request parameters are rejected when nested inside expressions that SQLPage explicitly emulates for sqlpage.* function arguments. This affects all SQLPage functions, not only send_mail.
Minimal side-effect-free reproducer:
select sqlpage.url_encode(
json_object('recipient', :recipient)
);
Posting recipient=a@example.com produces:
Unsupported sqlpage function argument:
sqlpage.url_encode(JSON_OBJECT('recipient', CAST(?1 AS TEXT)))
"CAST(?1 AS TEXT)" is an sql expression, which cannot be passed as a nested sqlpage function argument.
Expected: :recipient remains a request-backed parameter while json_object is evaluated by SQLPage, and the resulting JSON is passed to sqlpage.url_encode.
Scope verified from the extraction pipeline and targeted experiments:
- Affects
:post, $post_or_get, and query-only request parameters.
- Affects parameters nested in the supported
json_object/json_array variants, concat, coalesce, ||, and compositions of those expressions.
- Affects any
sqlpage.* function and both SELECT and SET pre-query calls.
- Applies to every database dialect: generated placeholders are always wrapped in
CAST(...); only placeholder syntax and cast type vary.
- Direct arguments such as
sqlpage.url_encode(:recipient) work.
- Literals and nested SQLPage functions work.
- Arbitrary database expressions remain intentionally unsupported and are not part of this bug.
- Assigning the database expression first is a workaround:
set payload = json_object('recipient', :recipient);
select sqlpage.url_encode($payload);
Root cause: ParameterExtractor rewrites nested request parameters before converting the enclosing SQLPage function. make_placeholder_for_index wraps generated placeholders in a dialect-specific CAST. The nested argument converter supports raw placeholders and the explicitly emulated expressions, but has no Expr::Cast handling, so it mistakes SQLPage's own generated cast for an unsupported database expression.
Existing tests cover direct request arguments and intentional rejection of arbitrary SQL expressions, but not a request parameter nested in an emulated expression.
I searched open and closed issues for nested SQLPage arguments, json_object, generated casts, and function arguments. #708 concerns PostgreSQL KEY VALUE JSON syntax and is unrelated.
Request parameters are rejected when nested inside expressions that SQLPage explicitly emulates for
sqlpage.*function arguments. This affects all SQLPage functions, not onlysend_mail.Minimal side-effect-free reproducer:
Posting
recipient=a@example.comproduces:Expected:
:recipientremains a request-backed parameter whilejson_objectis evaluated by SQLPage, and the resulting JSON is passed tosqlpage.url_encode.Scope verified from the extraction pipeline and targeted experiments:
:post,$post_or_get, and query-only request parameters.json_object/json_arrayvariants,concat,coalesce,||, and compositions of those expressions.sqlpage.*function and bothSELECTandSETpre-query calls.CAST(...); only placeholder syntax and cast type vary.sqlpage.url_encode(:recipient)work.Root cause:
ParameterExtractorrewrites nested request parameters before converting the enclosing SQLPage function.make_placeholder_for_indexwraps generated placeholders in a dialect-specificCAST. The nested argument converter supports raw placeholders and the explicitly emulated expressions, but has noExpr::Casthandling, so it mistakes SQLPage's own generated cast for an unsupported database expression.Existing tests cover direct request arguments and intentional rejection of arbitrary SQL expressions, but not a request parameter nested in an emulated expression.
I searched open and closed issues for nested SQLPage arguments,
json_object, generated casts, and function arguments. #708 concerns PostgreSQLKEY VALUEJSON syntax and is unrelated.