@@ -77,7 +77,14 @@ configuration entry.
7777
7878### Sources
7979
80- - The ` FROM ` item must be a table of that schema, optionally with an alias.
80+ - The ` FROM ` item must be a table of that schema, optionally with an alias. A
81+ ` LATERAL ` source is rejected as ` LATERAL is not supported. ` and a subquery
82+ source as ` A subquery in FROM is not supported. `
83+ - A ` FROM ` or ` JOIN ` source that names a ` WITH ` clause of its own query, which
84+ in PostgreSQL reads that query and not the table of the same name, is rejected
85+ as ` A common table expression used as a source is not supported: <name> `
86+ before the schema is consulted, so a ` WITH ` clause named like a table never
87+ reads that table.
8188- sqlcj models one namespace, so a source resolves by its unqualified name:
8289 ` users ` , ` public.users ` , and ` "public"."users" ` are one table, matched without
8390 SQL identifier delimiters and case-insensitively. A source qualified with any
@@ -87,10 +94,21 @@ configuration entry.
8794 not followed; see
8895 [ Schema Snapshot Input] ( postgresql.md#schema-snapshot-input ) .
8996- A table may be joined with ` JOIN ` , ` INNER JOIN ` , ` LEFT JOIN ` , or
90- ` LEFT OUTER JOIN ` , and inner and left joins may be chained in any order. A
91- comma-separated source list and every other join modifier — ` RIGHT ` , ` FULL ` , a
92- bare ` OUTER ` , ` CROSS ` , ` NATURAL ` , ` SEMI ` , ` APPLY ` , ` STRAIGHT ` , ` GLOBAL ` , a
93- join hint, and ` USING (...) ` — are rejected.
97+ ` LEFT OUTER JOIN ` , and inner and left joins may be chained in any order.
98+ - Every other join is rejected, and a join that can be written as a supported
99+ one says what to write:
100+ - ` FULL JOIN is not supported. `
101+ - ` RIGHT JOIN is not supported; write a LEFT JOIN with the two sources swapped. `
102+ - ` CROSS JOIN is not supported; write JOIN ... ON TRUE. `
103+ - ` NATURAL JOIN is not supported; write the join condition with ON. `
104+ - ` JOIN ... USING is not supported; write the join condition with ON. `
105+ - ` A comma-separated FROM list is not supported; write JOIN ... ON with the join condition. `
106+
107+ A ` LATERAL ` or subquery join source is reported as the same unsupported
108+ source as a ` FROM ` item of that kind, before the join kind, and every
109+ remaining modifier — a bare ` OUTER ` , ` SEMI ` , ` APPLY ` , ` STRAIGHT ` , ` GLOBAL ` ,
110+ and a join hint — is rejected as
111+ ` Only unmodified INNER JOIN and LEFT JOIN clauses are supported. `
94112- Each join requires exactly one ` ON ` condition, which may be any boolean
95113 expression, including ` ON TRUE ` . Its column references, qualified or not,
96114 resolve against the sources introduced up to and including the joined source,
@@ -119,7 +137,10 @@ configuration entry.
119137 unknown column is rejected.
120138- A result expression that is not a direct column, a wildcard, the scalar count
121139 below, or the cast projection below — such as another function call, an
122- arithmetic expression, or a literal — is rejected.
140+ arithmetic expression, or a literal — is rejected as
141+ `Cannot infer the type of <expression > in SELECT; state it with a cast and an
142+ alias, such as SUM(amount)::numeric AS total`, naming the expression as the
143+ query writes it, on one line.
123144- An explicit alias names the result column, so ` SELECT id AS author_id `
124145 generates the record component ` authorId ` while the component's type still
125146 comes from the column and its nullability from the column and its source.
@@ -538,12 +559,18 @@ value is read back with its declared type. A clause that is exactly `*` returns
538559the target table's shared ` <TableName>Row ` record, while a ` RETURNING ` column
539560list keeps the query's own ` <QueryName>Result ` record.
540561
541- An aliased ` * ` , a computed ` RETURNING ` item with or without an alias, a
542- qualified ` table.* ` , an unknown column, and ` RETURNING ` on ` :exec ` are rejected.
543- Multi-row ` VALUES ` , ` INSERT ... SELECT ` , ` UPDATE ... FROM ` , ` DELETE ... USING ` ,
544- and common table expressions are rejected in a returning write, while an
545- accepted ` ON CONFLICT ` clause is analyzed the same way with and without
546- ` RETURNING ` .
562+ An aliased ` * ` , a qualified ` table.* ` , an unknown column, and ` RETURNING ` on
563+ ` :exec ` are rejected, and a computed ` RETURNING ` item, with or without an alias,
564+ is rejected as
565+ ` Unsupported RETURNING item <expression>; return columns of the target table or * ` ,
566+ naming the expression as the query writes it, on one line. A cast does not make
567+ such an item supported, because a ` RETURNING ` clause returns columns of the
568+ target table.
569+
570+ Multi-row ` VALUES ` , ` INSERT ... SELECT ` , ` UPDATE ... FROM ` , and
571+ ` DELETE ... USING ` are rejected in every write, and common table expressions in
572+ a returning write, while an accepted ` ON CONFLICT ` clause is analyzed the same
573+ way with and without ` RETURNING ` .
547574
548575## Parameters
549576
@@ -869,20 +896,34 @@ the documented shapes are contract, tested, and safe to rely on.
869896
870897### Rejected
871898
872- Rejection stops the run with a diagnostic naming the query source, the query
873- name, and its header line.
874-
899+ Rejection stops the run with one diagnostic line naming the query source, the
900+ query name, and its header line.
901+
902+ - A statement that is not a ` SELECT ` , an ` INSERT ` , an ` UPDATE ` , or a ` DELETE ` ,
903+ including ` VALUES ` , ` TABLE ` , ` MERGE ` , ` TRUNCATE ` , ` EXPLAIN ` , ` EXECUTE ` , a DDL
904+ statement such as ` CREATE TABLE ` , and a ` SET ` statement, each rejected as
905+ ` Only SELECT, INSERT, UPDATE, and DELETE statements are supported. ` before the
906+ annotation is checked. A parenthesized ` SELECT ` is the query it encloses, at
907+ any depth, so it is analyzed as that query.
908+ - A set operation, ` UNION ` , ` UNION ALL ` , ` INTERSECT ` , or ` EXCEPT ` , written
909+ directly or inside parentheses, rejected as
910+ ` UNION, INTERSECT, and EXCEPT are not supported; write one query per SELECT. `
875911- A result expression that is not a direct column, a wildcard, an aliased
876912 ` COUNT(*) ` , or an aliased cast, including another function call, another
877913 aggregate, and a literal. A ` COUNT(*) ` without an alias, a cast projection
878914 without an alias, and a cast projection whose type sqlcj does not map each
879915 fail with their own diagnostic, while ` COUNT(column) ` ,
880916 ` COUNT(DISTINCT column) ` , ` COUNT(t.*) ` , ` pg_catalog.count(*) ` ,
881- ` lower(name) AS n ` , and the ` FILTER ` and ` OVER ` forms keep the
882- unsupported-expression rejection.
883- - A ` FROM ` item that is not a table, a comma-separated source list, a join that
884- is not a plain inner or left join, a join that does not carry exactly one
885- ` ON ` condition, and a set operation such as ` UNION ` .
917+ ` lower(name) AS n ` , ` name || 'x' AS n ` , and the ` FILTER ` and ` OVER ` forms are
918+ rejected as `Cannot infer the type of COUNT(* ) OVER () in SELECT; state it
919+ with a cast and an alias, such as SUM(amount)::numeric AS total`.
920+ - A ` FROM ` or ` JOIN ` source that is not a table of the schema: a ` LATERAL `
921+ source, a subquery source, and a source that names a ` WITH ` clause of its own
922+ query, each with the diagnostic [ Sources] ( #sources ) states.
923+ - A join kind outside a plain inner or left join, each with the diagnostic
924+ [ Sources] ( #sources ) states, which says what to write instead for ` RIGHT ` ,
925+ ` CROSS ` , ` NATURAL ` , ` USING ` , and a comma-separated ` FROM ` list, and a join
926+ that does not carry exactly one ` ON ` condition.
886927- An uncast placeholder in a location sqlcj does not analyze, including
887928 ` ORDER BY $1 ` , a projection such as ` SELECT $1 AS x ` , a grouping list, a
888929 ` HAVING ` predicate, a subquery, a
@@ -925,10 +966,9 @@ name, and its header line.
925966 placeholder, each of them also as the operand of a cast, such as
926967 ` :"x"::text ` , ` &x::text ` , or ` :a.b::text ` , one name whose occurrences have
927968 conflicting types, and non-contiguous or non-positive placeholder indexes.
928- - An ` INSERT ` without an explicit column list, with more than one ` VALUES ` row,
929- or built from a ` SELECT ` ; an ` UPDATE ` assignment that sets a column list, such
930- as ` SET (name, active) = ('a', TRUE) ` ; and a written column that the table does
931- not declare.
969+ - An ` INSERT ` without an explicit column list; an ` UPDATE ` assignment that sets
970+ a column list, such as ` SET (name, active) = ('a', TRUE) ` ; and a written
971+ column that the table does not declare.
932972- A conflict clause outside the accepted upsert shape, on an ` :exec ` and on a
933973 returning insert alike: a ` DO UPDATE ` without a conflict target, which is
934974 rejected naming the two targets it accepts, and a target element that is an
@@ -938,11 +978,27 @@ name, and its header line.
938978 unqualified conflict-predicate column that both the inserted table and
939979 ` EXCLUDED ` carry, or a predicate column qualified with a name its own
940980 predicate does not read, such as ` EXCLUDED ` in a conflict-target predicate.
941- - An aliased ` * ` , a computed, qualified-wildcard, or unknown ` RETURNING ` item;
942- ` RETURNING ` on ` :exec ` ; and ` UPDATE ... FROM ` , ` DELETE ... USING ` , or a common
943- table expression in a returning write.
981+ - An aliased ` * ` , a computed, qualified-wildcard, or unknown ` RETURNING ` item,
982+ and ` RETURNING ` on ` :exec ` ; a computed item is rejected as
983+ [ ` RETURNING ` ] ( #returning ) states.
984+ - The shapes that read or write more than the one analyzed table, on an
985+ ` :exec ` and on a returning write alike: an ` INSERT ` with more than one
986+ ` VALUES ` row, rejected as ` INSERT requires a single VALUES row. ` ; an
987+ ` INSERT ... SELECT ` , rejected as ` INSERT ... SELECT is not supported. ` ; an
988+ ` UPDATE ... FROM ` or joined ` UPDATE ` , rejected as
989+ ` UPDATE ... FROM and joined updates are not supported. ` ; and a
990+ ` DELETE ... USING ` or joined ` DELETE ` , rejected as
991+ ` DELETE ... USING and joined deletes are not supported. ` A common table
992+ expression in a returning write is rejected too.
944993- Any annotation other than ` :one ` , ` :optional ` , ` :many ` , and ` :exec ` .
945994
995+ An unexpected failure of schema parsing, query analysis, or generation is not a
996+ diagnostic of the compiled input: it is reported as one line that names the
997+ step's own input and ends ` ; please report this sqlcj defect ` , such as
998+ `Internal error while analyzing query 'GetUser' in queries.sql at line 1;
999+ please report this sqlcj defect`. The line carries neither the message nor the
1000+ class of the failure, because neither describes the compiled input.
1001+
9461002### Not analyzed
9471003
9481004These are outside the subset and are not part of the contract. sqlcj neither
@@ -956,8 +1012,8 @@ them:
9561012 ` LIKE ` with a literal pattern, a range whose bounds are both literal such as
9571013 ` id BETWEEN 1 AND 10 ` , ` IN ` with a subquery, or ` = ANY ` over something other
9581014 than a placeholder, such as ` id = ANY('{1,2}') ` ,
959- - common table expressions , and subqueries outside the ` FROM ` item,
960- - ` UPDATE ... FROM` and ` DELETE ... USING ` on a non-returning ` :exec ` write ,
1015+ - a common table expression whose name no source reads , and subqueries outside
1016+ the ` FROM ` item ,
9611017- the unique index an accepted ` ON CONFLICT ` target matches, the constraint an
9621018 ` ON CONSTRAINT ` target names, the column references of an ` EXCLUDED ` value
9631019 that is not exactly ` EXCLUDED.column ` , and a conflict-predicate column that
@@ -1274,6 +1330,41 @@ Writes and `RETURNING`:
12741330 insert, a returning named insert, and an ` :exec ` update against
12751331 PostgreSQL 16.
12761332
1333+ Unsupported queries and internal errors:
1334+
1335+ - ` SqlcjCompilerIntegrationTest.shouldRejectUnsupportedQueryBeforeWriting `
1336+ covers the whole located diagnostic line, and the absence of generated
1337+ output, of each rejected input: the set operations written directly and in
1338+ parentheses, the ` VALUES ` , ` TABLE ` , ` MERGE ` , ` TRUNCATE ` , ` EXPLAIN ` ,
1339+ ` EXECUTE ` , ` CREATE TABLE ` , and ` SET ` statements, a window and a
1340+ concatenation projection, a computed ` RETURNING ` item, the ` RIGHT ` , ` FULL ` ,
1341+ ` USING ` , ` NATURAL ` , ` CROSS ` , comma, and ` LATERAL ` joins, a ` FROM ` subquery, a
1342+ common table expression used as a source and one named like a schema table,
1343+ a multi-row ` VALUES ` , an ` INSERT ... SELECT ` on ` :exec ` and with
1344+ ` RETURNING ` , and an ` :exec ` ` UPDATE ... FROM ` , comma-joined ` UPDATE ` ,
1345+ ` DELETE ... USING ` , and joined ` DELETE ` , while
1346+ ` QueryAnalyzerTest.shouldRejectUnsupportedCountProjectionForm ` ,
1347+ ` QueryAnalyzerTest.shouldRejectUnsupportedJoinModifier ` ,
1348+ ` QueryAnalyzerTest.shouldRejectUnsupportedReturningItem ` ,
1349+ ` QueryAnalyzerTest.shouldRejectExcludedReturningWriteForm ` , and
1350+ ` QueryAnalyzerTest.shouldRejectParenthesizedInsertQuerySource ` cover the
1351+ projection, join, and write rejections per form, including the modifiers that
1352+ keep the general join rejection, and
1353+ ` QueryAnalyzerTest.shouldAnalyzeParenthesizedSelectAsTheQueryItEncloses ` and
1354+ ` QueryAnalyzerTest.shouldAnalyzeInsertWhoseValuesRowIsParenthesized ` cover
1355+ the doubly parenthesized read that analyzes like the query it encloses and
1356+ the parenthesized ` VALUES ` row that analyzes like the row it writes.
1357+ - ` SqlcjCompilerIntegrationTest.shouldReportAnUnexpectedSchemaFailureAsAnInternalError ` ,
1358+ ` SqlcjCompilerIntegrationTest.shouldReportAnUnexpectedAnalysisFailureAsAnInternalError ` ,
1359+ ` SqlcjCompilerIntegrationTest.shouldReportAnUnexpectedGenerationFailureAsAnInternalError ` ,
1360+ and
1361+ ` SqlcjCompilerIntegrationTest.shouldReportAnUnexpectedSharedGenerationFailureAsAnInternalError `
1362+ inject an unexpected failure into the schema, analysis, repository
1363+ generation, and shared row-record steps and cover the exact internal-error
1364+ line of each, that it carries neither the failure's message nor its class,
1365+ including a blank-message unsupported-operation failure and a
1366+ ` NumberFormatException ` , and that no file is written.
1367+
12771368Parameters:
12781369
12791370- ` QueryAnalyzerTest.shouldResolveBindingIndexesInTextualOrder ` ,
0 commit comments