@@ -602,7 +602,19 @@ string literal, a quoted identifier, or a comment is not a parameter.
602602 such as ` :"x" ` , and an ` &name ` placeholder.
603603- A placeholder that neither a clause nor a cast types — for example
604604 ` ORDER BY $1 ` , ` lower(name) = :name ` , or ` (:x)::int ` , whose placeholder is not
605- the direct operand of its cast — is rejected rather than left unbound.
605+ the direct operand of its cast — is rejected rather than left unbound, as
606+ `Cannot infer the type of : name in lower(name) = : name ; state it with a cast,
607+ such as :name::text`. The diagnostic writes the placeholder as the query
608+ spells it, ` $1 ` or ` :name ` , and names where the query writes it: the smallest
609+ expression written around the placeholder, on one line, with whatever
610+ separates two tokens written as one space. A concatenated pattern is
611+ therefore named in its whole ` LIKE ` predicate and an ` IN ` list in its ` IN `
612+ predicate, while a placeholder written inside no expression stands alone in
613+ its clause and is named by the keyword that opens that clause, in upper case:
614+ ` SELECT ` , ` DISTINCT ON ` , ` ON ` , ` WHERE ` , ` GROUP BY ` , ` HAVING ` , ` ORDER BY ` ,
615+ ` LIMIT ` , ` OFFSET ` , ` FETCH ` , ` VALUES ` , ` SET ` , or ` RETURNING ` . One query reports
616+ its first untyped placeholder in textual order, after the rules of its clauses
617+ have run, so a clause's own rejection comes first.
606618
607619### Logical order versus textual order
608620
@@ -882,7 +894,12 @@ name, and its header line.
882894 ` LIMIT $1 + 1 ` or ` OFFSET $1 + 1 ` , a ` LIMIT a, b ` row count such as
883895 ` LIMIT 5, $1 ` , a placeholder inside a write value such as
884896 ` COALESCE($2, bio) ` , and a ` FETCH FIRST $1 ROWS ONLY ` clause, so dynamic ` IN `
885- expansion is unavailable. A cast states the type of such a placeholder in
897+ expansion is unavailable. Each is rejected as `Cannot infer the type of $1 in
898+ $1 + 1; state it with a cast, such as $1::text`, which names the placeholder
899+ as the query spells it and, after ` in ` , the smallest expression the query
900+ writes around it, on one line, or the upper-case keyword of the clause the
901+ placeholder stands alone in, such as ` ORDER BY ` for ` ORDER BY $1 ` and
902+ ` SELECT ` for ` SELECT $1 AS x ` . A cast states the type of such a placeholder in
886903 every one of those locations, and it does not accept a placeholder that is not
887904 the direct operand of its cast, such as ` (:x)::int ` .
888905- A cast type sqlcj does not map, such as ` $1::interval ` or a
@@ -1220,7 +1237,7 @@ Writes and `RETURNING`:
12201237 and ` QueryAnalyzerTest.shouldRejectAnUnknownConflictColumn ` cover the accepted
12211238 conflict target, both actions, the ` EXCLUDED ` , placeholder, cast, and
12221239 computed assignments, the binding order after the ` VALUES ` placeholders, the
1223- returning row type, and the conflict forms, unaccounted placeholder, and
1240+ returning row type, and the conflict forms, untyped placeholder, and
12241241 unknown target, assigned, and ` EXCLUDED ` columns that are rejected on an
12251242 ` :exec ` and on a returning insert alike, and
12261243 ` PostgresIntegrationTest.shouldExecuteGeneratedUpsertsAgainstPostgres `
@@ -1268,6 +1285,15 @@ Parameters:
12681285 ` QueryAnalyzerTest.shouldRejectAnonymousParameter ` , and
12691286 ` QueryAnalyzerTest.shouldKeepPlaceholderTextThatIsNotAParameter ` cover
12701287 ordering, repetition, and rejection.
1288+ - ` QueryAnalyzerTest.shouldRejectAnUntypedPlaceholder ` ,
1289+ ` QueryAnalyzerTest.shouldRejectAnUntypedPlaceholderOfAConcatenatedPattern ` ,
1290+ ` QueryAnalyzerTest.shouldRejectTheFirstUntypedPlaceholderOfAQuery ` ,
1291+ ` SqlParserTest.shouldReportTheEnclosingExpressionOfAPlaceholder ` ,
1292+ ` SqlParserTest.shouldReportTheClauseOfAPlaceholderThatStandsAlone ` , and
1293+ ` SqlcjCompilerIntegrationTest.shouldRejectUntypedPlaceholderBeforeWriting `
1294+ cover the untyped-placeholder diagnostic, in both placeholder forms, with the
1295+ enclosing expression and the clause keyword it names, the first occurrence it
1296+ reports, and the whole located line of a compiled source.
12711297- ` SqlParserTest.shouldCompileNamedParametersByFirstOccurrence ` ,
12721298 ` SqlParserTest.shouldCompileNamesThatDifferInCaseAsDistinctParameters ` ,
12731299 ` SqlParserTest.shouldReportBothPlaceholderFormsOfOneSource ` ,
0 commit comments