@minnowdb/core 0.7.2 → 0.7.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,48 @@
12
12
  "status": "supported",
13
13
  "example": "SELECT amount AS total FROM rows"
14
14
  },
15
+ {
16
+ "id": "select.all",
17
+ "status": "supported",
18
+ "example": "SELECT ALL region, amount FROM rows",
19
+ "notes": "SELECT ALL names the default: every row, duplicates kept. ALL is also accepted inside an aggregate, as in COUNT(ALL amount)."
20
+ },
21
+ {
22
+ "id": "select.distinct-on",
23
+ "status": "supported",
24
+ "example": "SELECT DISTINCT ON (region) region, amount FROM rows ORDER BY region, amount DESC",
25
+ "notes": "PostgreSQL's DISTINCT ON (expressions) keeps the first row of each group in ORDER BY order. It lowers to ROW_NUMBER() OVER (PARTITION BY expressions ORDER BY …) = 1 over the block, so ORDER BY may name output aliases and columns the select list omits, and LIMIT and OFFSET apply to the kept rows."
26
+ },
27
+ {
28
+ "id": "select.locking-clause",
29
+ "status": "supported",
30
+ "example": "SELECT region, amount FROM rows FOR UPDATE",
31
+ "notes": "FOR UPDATE, FOR NO KEY UPDATE, FOR SHARE, and FOR KEY SHARE, with OF, NOWAIT, or SKIP LOCKED, are accepted and ignored: row locks coordinate concurrent sessions, and a single-session engine has none to coordinate."
32
+ },
33
+ {
34
+ "id": "select.table-command",
35
+ "status": "supported",
36
+ "example": "TABLE rows",
37
+ "notes": "TABLE name is the standard's spelling of SELECT * FROM name."
38
+ },
39
+ {
40
+ "id": "literal.string-spellings",
41
+ "status": "supported",
42
+ "example": "SELECT E'tab\\\\there' AS escaped, $$dollar 'quoted'$$ AS plain, 5. AS five, .5 AS half FROM rows",
43
+ "notes": "E'…' strings take C-style backslash escapes (\\\\n, \\\\t, \\\\xHH, \\\\uHHHH, octal), $tag$…$tag$ strings are taken verbatim, and 5. and .5 spell 5.0 and 0.5, as in PostgreSQL."
44
+ },
45
+ {
46
+ "id": "predicate.like-operators",
47
+ "status": "supported",
48
+ "example": "SELECT region FROM rows WHERE region ~~ 'w%' OR region !~~* 'E%'",
49
+ "notes": "~~, !~~, ~~*, and !~~* are PostgreSQL's operator spellings of LIKE, NOT LIKE, ILIKE, and NOT ILIKE."
50
+ },
51
+ {
52
+ "id": "select.label-without-as",
53
+ "status": "supported",
54
+ "example": "SELECT amount total, region \"area\" FROM rows",
55
+ "notes": "A column label needs no AS, as in PostgreSQL and SQLite. The label is any identifier that no clause or operator keyword can claim; a quoted identifier is always a label."
56
+ },
15
57
  {
16
58
  "id": "select.wildcard",
17
59
  "status": "supported",
@@ -37,7 +79,7 @@
37
79
  "id": "expression.round",
38
80
  "status": "supported",
39
81
  "example": "SELECT ROUND(amount / 3, 2) AS thirds FROM rows",
40
- "notes": "Precision truncates to an integer and clamps to 0..30; halfway values round away from zero, matching SQLite."
82
+ "notes": "Over a double, precision truncates to an integer and clamps to 0..30, and halfway values round away from zero, matching SQLite. Over an exact NUMERIC the result is PostgreSQL's numeric ROUND: exact, half away from zero, a negative digit count rounds left of the decimal point, and a literal digit count is the result's display scale."
41
83
  },
42
84
  {
43
85
  "id": "literal.string",
@@ -186,6 +228,12 @@
186
228
  "status": "supported",
187
229
  "example": "WITH west AS (SELECT amount FROM rows WHERE region = 'west') SELECT COUNT(*) AS count FROM west"
188
230
  },
231
+ {
232
+ "id": "cte.materialized",
233
+ "status": "supported",
234
+ "example": "WITH w AS MATERIALIZED (SELECT region, amount FROM rows) SELECT region FROM w",
235
+ "notes": "[NOT] MATERIALIZED is PostgreSQL's planner hint on a CTE; the block is planned the same way either way."
236
+ },
189
237
  {
190
238
  "id": "cte.chained",
191
239
  "status": "supported",
@@ -269,6 +317,25 @@
269
317
  "example": "DELETE FROM keyed WHERE score < 0",
270
318
  "notes": "Requires a unique-key table."
271
319
  },
320
+ {
321
+ "id": "mutation.update-from",
322
+ "status": "supported",
323
+ "example": "UPDATE keyed SET score = r.amount FROM (SELECT 'x' AS name, 5 AS amount) r WHERE r.name = keyed.name",
324
+ "notes": "UPDATE … FROM joins extra sources — tables, views, or derived tables — to the target as PostgreSQL does; the assignments and predicates may read them. A target row matched by several source rows is updated once, from its first match."
325
+ },
326
+ {
327
+ "id": "mutation.delete-using",
328
+ "status": "supported",
329
+ "example": "DELETE FROM keyed USING (SELECT 'y' AS name) gone WHERE gone.name = keyed.name",
330
+ "notes": "DELETE … USING joins extra sources to the target as PostgreSQL does; a target row matched by several source rows is deleted once."
331
+ },
332
+ {
333
+ "id": "mutation.truncate",
334
+ "status": "supported",
335
+ "setup": ["INSERT INTO keyed (name, score) VALUES ('z', 3)"],
336
+ "example": "TRUNCATE TABLE keyed",
337
+ "notes": "TRUNCATE [TABLE] [ONLY] name [RESTART IDENTITY | CONTINUE IDENTITY] [RESTRICT] removes every row of one table, the same as an unfiltered DELETE, so like DELETE it needs a table with a unique key. Several tables in one statement and CASCADE are refused; truncate each table separately."
338
+ },
272
339
  {
273
340
  "id": "mutation.returning",
274
341
  "status": "supported",
@@ -368,6 +435,12 @@
368
435
  "status": "supported",
369
436
  "example": "SELECT region, BM25(region) AGAINST 'west' AS score FROM rows WHERE MATCH(region) AGAINST 'west' ORDER BY score DESC"
370
437
  },
438
+ {
439
+ "id": "function.regexp-substring",
440
+ "status": "supported",
441
+ "example": "SELECT SUBSTRING(region FROM '(e.)') AS part, SUBSTRING(region FROM 2 FOR 2) AS middle, TO_HEX(255) AS hex, QUOTE_LITERAL(region) AS quoted, QUOTE_IDENT('Mixed Name') AS ident FROM rows",
442
+ "notes": "SUBSTRING(text FROM 'pattern') is PostgreSQL's POSIX-regex form: the first match, or its first parenthesized group; a number after FROM is the ordinary start position. TO_HEX renders an integer in hexadecimal; QUOTE_LITERAL and QUOTE_IDENT quote a value or a name for use in SQL text."
443
+ },
371
444
  {
372
445
  "id": "order-by.expression",
373
446
  "status": "supported",
@@ -489,6 +562,13 @@
489
562
  "example": "SELECT r.amount FROM rows r WHERE r.amount = 3 OR r.amount > ALL (SELECT q.amount FROM rows q WHERE q.region = r.region)",
490
563
  "notes": "Top-level WHERE uses semi/anti joins. Nested expressions group true, false, unknown, and empty-set counts per distinct outer probe tuple."
491
564
  },
565
+ {
566
+ "id": "subquery.array-constructor",
567
+ "status": "unsupported",
568
+ "example": "SELECT ARRAY(SELECT amount FROM rows) AS amounts FROM rows",
569
+ "error": "Expected )",
570
+ "notes": "ARRAY(subquery) is not supported; a scalar subquery with json_agg builds the same list as a JSON document."
571
+ },
492
572
  {
493
573
  "id": "cte.recursive",
494
574
  "status": "supported",
@@ -670,6 +750,24 @@
670
750
  "example": "CREATE TABLE made (id INTEGER PRIMARY KEY, label TEXT NOT NULL, at TIMESTAMP)",
671
751
  "notes": "Common PostgreSQL type names map onto Minnow's four stored value kinds (widths parse and are ignored); one PRIMARY KEY or UNIQUE column becomes the row-addressing key. ALTER TABLE ADD/DROP COLUMN and DROP TABLE are tracked separately."
672
752
  },
753
+ {
754
+ "id": "ddl.type-spellings",
755
+ "status": "supported",
756
+ "example": "CREATE TABLE spelled (id int4 PRIMARY KEY, name character varying(10), note character varying, at timestamp with time zone, seen timestamp without time zone, big int8, small int2, f float8, r float4, flag bool)",
757
+ "notes": "PostgreSQL's internal type names (int2, int4, int8, float4, float8, bool) and its multi-word spellings (character varying, timestamp with time zone, timestamp without time zone, time with time zone) map onto the same storage as INTEGER, BIGINT, DOUBLE PRECISION, BOOLEAN, VARCHAR, and TIMESTAMPTZ. pg_dump and migration tools emit these spellings."
758
+ },
759
+ {
760
+ "id": "ddl.temporary-table",
761
+ "status": "supported",
762
+ "example": "CREATE TEMP TABLE scratch (id INTEGER PRIMARY KEY, note TEXT)",
763
+ "notes": "TEMP, TEMPORARY, UNLOGGED, GLOBAL, and LOCAL are accepted and ignored: every table lives in the one database with one durability, so the modifiers document intent and change nothing."
764
+ },
765
+ {
766
+ "id": "ddl.named-column-constraint",
767
+ "status": "supported",
768
+ "example": "CREATE TABLE guarded (id INTEGER PRIMARY KEY, n INTEGER CONSTRAINT guarded_n_positive CHECK (n > 0))",
769
+ "notes": "CONSTRAINT name before a column's CHECK or REFERENCES names that constraint, as it does at table level; the name of a column's PRIMARY KEY, UNIQUE, or NOT NULL is informational."
770
+ },
673
771
  {
674
772
  "id": "mutation.update-keyless",
675
773
  "status": "unsupported",
@@ -723,6 +821,18 @@
723
821
  "example": "SELECT region || '-' || label AS tag FROM dims",
724
822
  "notes": "|| concatenates strings and propagates NULL; non-string operands are a type error. One operand must be ordinary text, matching PostgreSQL's operator resolution: array and JSONB || are structural concatenation (see array.concat and json.concat), and two non-text domain operands have no || operator. A domain value concatenated with text renders in its Minnow text form."
725
823
  },
824
+ {
825
+ "id": "expression.integer-division",
826
+ "status": "supported",
827
+ "example": "SELECT 7 / 2 AS quotient, -7 / 2 AS negative, 7.0 / 2 AS exact, CAST(amount AS INTEGER) / 2 AS half FROM rows",
828
+ "notes": "Division follows PostgreSQL's typing. Two integer operands — INTEGER, BIGINT, or SMALLINT columns, integer constants, COUNT, integer CASTs, and integer arithmetic or aggregates over them — divide as integers, truncating toward zero: 7 / 2 is 3 and -7 / 2 is -3. A double, NUMERIC, or decimal constant operand makes the quotient fractional: 7.0 / 2 is 3.5. A bound parameter takes the type of its integer partner, so id / $1 truncates when $1 is bound to an integer."
829
+ },
830
+ {
831
+ "id": "expression.untyped-arithmetic",
832
+ "status": "supported",
833
+ "example": "SELECT '5' + 1 AS six, 2 * '3' AS six_again FROM rows",
834
+ "notes": "An untyped string constant beside a number in + - * / % is read as a number, as PostgreSQL types the unknown literal by its partner. Text that is not a number is still an error."
835
+ },
726
836
  {
727
837
  "id": "expression.modulo",
728
838
  "status": "supported",
@@ -854,6 +964,32 @@
854
964
  "example": "BEGIN",
855
965
  "notes": "Holds the same scope `write()` opens between statements instead of around a callback: writes stage into it, reads see what it staged, and COMMIT publishes them together. Schema changes are refused inside one, because the catalog commits outside the scope and a rollback could not take them back. A transaction left untouched past the idle bound rolls itself back, so an abandoned BEGIN cannot hold storage forever."
856
966
  },
967
+ {
968
+ "id": "transaction.end",
969
+ "status": "supported",
970
+ "setup": ["BEGIN"],
971
+ "example": "END",
972
+ "notes": "END and END TRANSACTION commit, as COMMIT does."
973
+ },
974
+ {
975
+ "id": "transaction.abort",
976
+ "status": "supported",
977
+ "setup": ["BEGIN"],
978
+ "example": "ABORT",
979
+ "notes": "ABORT rolls back, as ROLLBACK does."
980
+ },
981
+ {
982
+ "id": "transaction.session-settings",
983
+ "status": "supported",
984
+ "example": "SET search_path TO public",
985
+ "notes": "SET [SESSION | LOCAL] name TO value, SET TRANSACTION …, and RESET name are accepted and ignored: an embedded single-session engine has no search path, timeouts, encodings, or isolation levels to configure, and drivers and migration tools issue these on every connection. SET TIME ZONE accepts only UTC, because every datetime is an instant in UTC; another zone is refused rather than silently ignored."
986
+ },
987
+ {
988
+ "id": "transaction.show-setting",
989
+ "status": "supported",
990
+ "example": "SHOW server_version",
991
+ "notes": "SHOW returns the engine's fixed answer for the settings drivers and tools read on connection — search_path, server_version, timezone, transaction_isolation, client_encoding, and the like — as one row. An unknown setting is an error, as in PostgreSQL."
992
+ },
857
993
  {
858
994
  "id": "transaction.commit",
859
995
  "status": "supported",
@@ -870,8 +1006,8 @@
870
1006
  "id": "transaction.isolation-level",
871
1007
  "status": "unsupported",
872
1008
  "example": "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE",
873
- "error": "Expected SELECT, found SET",
874
- "notes": "The engine has one isolation level. Every read runs against a single version and every write scope commits atomically, so there is no weaker mode to relax into and no stronger one to ask for."
1009
+ "error": "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE is not supported: the engine has one isolation level",
1010
+ "notes": "Every transaction reads one snapshot and commits atomically, which satisfies READ UNCOMMITTED, READ COMMITTED, and REPEATABLE READ, so those levels are accepted in SET TRANSACTION and BEGIN. SERIALIZABLE promises more than that and is refused rather than silently downgraded."
875
1011
  },
876
1012
  {
877
1013
  "id": "function.char-length",
@@ -933,6 +1069,12 @@
933
1069
  "status": "supported",
934
1070
  "example": "SELECT y.a AS a FROM rows AS y(a, b, c, d)"
935
1071
  },
1072
+ {
1073
+ "id": "from.parenthesized-join",
1074
+ "status": "supported",
1075
+ "example": "SELECT COUNT(*) AS pairs FROM (rows r CROSS JOIN dims d)",
1076
+ "notes": "A parenthesized join group is accepted as the first source or as the operand of a comma, CROSS JOIN, or INNER JOIN, whose ON condition then filters the product. A group cannot take an alias, and LEFT, RIGHT, FULL, NATURAL, or USING joins onto a group are rejected: the flat join chain cannot treat the group as one side."
1077
+ },
936
1078
  {
937
1079
  "id": "aggregate.all-quantifier",
938
1080
  "status": "supported",
@@ -970,6 +1112,13 @@
970
1112
  "example": "SELECT rows.amount AS amount FROM rows NATURAL JOIN dims",
971
1113
  "notes": "The shared columns are compared but not merged, so an unqualified reference to one is ambiguous — qualify it. NATURAL RIGHT JOIN is rejected, because the right-join mirror rewrites the sources the shared-column search reads."
972
1114
  },
1115
+ {
1116
+ "id": "join.full-compound-on",
1117
+ "status": "unsupported",
1118
+ "example": "SELECT r.region FROM rows r FULL JOIN keyed k ON k.name = r.region AND k.score > 0",
1119
+ "error": "FULL JOIN requires a single equality ON condition",
1120
+ "notes": "FULL JOIN takes a single equality ON condition; move the extra condition into a derived source."
1121
+ },
973
1122
  {
974
1123
  "id": "datetime.current-date",
975
1124
  "status": "supported",
@@ -1030,6 +1179,13 @@
1030
1179
  "example": "SELECT 2.5e-1 AS quarter",
1031
1180
  "notes": "Scientific notation is a numeric constant, as in PostgreSQL. PostgreSQL renders every such constant as NUMERIC text; Minnow returns a number when the value reads back identically from one, and renders a constant that stays exact fully expanded, as PostgreSQL renders it."
1032
1181
  },
1182
+ {
1183
+ "id": "literal.bit-string",
1184
+ "status": "unsupported",
1185
+ "example": "SELECT B'101' AS bits FROM rows",
1186
+ "error": "Expected eof, found 101",
1187
+ "notes": "Bit-string literals and the BIT types are not supported."
1188
+ },
1033
1189
  {
1034
1190
  "id": "limit.with-ties",
1035
1191
  "status": "supported",
@@ -1089,6 +1245,30 @@
1089
1245
  "status": "supported",
1090
1246
  "example": "SELECT EVERY(amount > 1) AS all_positive FROM rows"
1091
1247
  },
1248
+ {
1249
+ "id": "json.agg-spellings",
1250
+ "status": "supported",
1251
+ "example": "SELECT region, JSON_AGG(amount ORDER BY amount) AS amounts, JSONB_AGG(amount ORDER BY amount DESC) AS amounts_desc FROM rows GROUP BY region",
1252
+ "notes": "json_agg and jsonb_agg are PostgreSQL's spellings of JSON_ARRAYAGG, with the same ORDER BY inside the call. jsonb and json produce the same document text."
1253
+ },
1254
+ {
1255
+ "id": "json.build-object",
1256
+ "status": "supported",
1257
+ "example": "SELECT JSON_BUILD_OBJECT('region', region, 'amount', amount) AS doc, JSONB_BUILD_ARRAY(amount, region) AS list FROM rows",
1258
+ "notes": "json_build_object, jsonb_build_object, json_build_array, and jsonb_build_array are PostgreSQL's spellings of JSON_OBJECT and JSON_ARRAY."
1259
+ },
1260
+ {
1261
+ "id": "json.to-json",
1262
+ "status": "supported",
1263
+ "example": "SELECT TO_JSON(amount) AS amount_doc, TO_JSONB(region) AS region_doc FROM rows",
1264
+ "notes": "to_json and to_jsonb render one SQL value as a JSON document: a number as itself, text as a JSON string, a boolean as true or false, NULL as the document null, and a JSON value as itself."
1265
+ },
1266
+ {
1267
+ "id": "json.row-reference",
1268
+ "status": "supported",
1269
+ "example": "SELECT r.region, JSON_AGG(r ORDER BY r.amount) AS rows_doc, JSON_AGG(ROW_TO_JSON(r) ORDER BY r.amount) AS row_docs FROM (SELECT region, amount FROM rows) r GROUP BY r.region",
1270
+ "notes": "A table alias used as a value is the row as a JSON object with the source's columns in order, as PostgreSQL treats it — the shape json_agg(agg) and to_json(obj) take in Kysely's jsonArrayFrom and jsonObjectFrom. Datetime members render as ISO 8601 with a Z suffix."
1271
+ },
1092
1272
  {
1093
1273
  "id": "json.value",
1094
1274
  "status": "supported",
@@ -1183,7 +1363,7 @@
1183
1363
  "id": "type.exact-numeric",
1184
1364
  "status": "supported",
1185
1365
  "example": "SELECT CAST(1.25 AS DECIMAL(12, 2)) AS amount",
1186
- "notes": "Exact through storage, arithmetic, comparison, aggregates, and windows. Division selects its result scale the way PostgreSQL does: at least sixteen significant digits, never fewer fractional digits than either operand, rounded half away from zero."
1366
+ "notes": "Exact through storage, arithmetic, comparison, aggregates, windows, and ROUND, TRUNC, ABS, FLOOR, CEIL, MOD, and SIGN. Addition, subtraction, remainder, and multiplication carry PostgreSQL's display scale (the larger operand scale, or their sum for a product); division selects its result scale the way PostgreSQL does: at least sixteen significant digits, never fewer fractional digits than either operand, rounded half away from zero. A plain-number fallback beside NUMERIC values, as in COALESCE(amount, 0), renders at its own scale."
1187
1367
  },
1188
1368
  {
1189
1369
  "id": "type.json-jsonb",
@@ -1286,6 +1466,22 @@
1286
1466
  "setup": ["BEGIN"],
1287
1467
  "notes": "SAVEPOINT, ROLLBACK TO, and RELEASE operate on staged transaction state."
1288
1468
  },
1469
+ {
1470
+ "id": "transaction.ddl-inside",
1471
+ "status": "unsupported",
1472
+ "setup": ["BEGIN"],
1473
+ "example": "CREATE TABLE inside (id INTEGER PRIMARY KEY)",
1474
+ "error": "CREATE TABLE is not allowed inside a transaction",
1475
+ "notes": "Schema statements are refused inside BEGIN … COMMIT: the catalog commits outside the scope, so a rollback could not take them back. Run DDL outside a transaction; migration tools that wrap migrations in one need that step split out."
1476
+ },
1477
+ {
1478
+ "id": "transaction.lock-table",
1479
+ "status": "unsupported",
1480
+ "setup": ["BEGIN"],
1481
+ "example": "LOCK TABLE keyed IN SHARE MODE",
1482
+ "error": "Expected SELECT, found LOCK",
1483
+ "notes": "LOCK TABLE is not supported; a single-session engine has no other session to lock against."
1484
+ },
1289
1485
  {
1290
1486
  "id": "privileges.grant",
1291
1487
  "status": "unsupported",
@@ -1354,6 +1550,20 @@
1354
1550
  "example": "SELECT JSON_ARRAYAGG(JSON_OBJECT('region' VALUE region)) AS regions FROM rows",
1355
1551
  "notes": "Supports DISTINCT and aggregate-local ORDER BY, embeds JSON-producing inputs as documents, includes SQL NULL as JSON null, and returns NULL for empty input. FILTER, window use, and explicit NULL/ABSENT clauses are not supported; input order is unspecified without ORDER BY."
1356
1552
  },
1553
+ {
1554
+ "id": "aggregate.array-agg",
1555
+ "status": "unsupported",
1556
+ "example": "SELECT region, array_agg(amount) AS amounts FROM rows GROUP BY region",
1557
+ "error": "Unsupported function: array_agg",
1558
+ "notes": "array_agg is not supported because arrays are not; json_agg (JSON_ARRAYAGG) collects a group into a JSON array instead."
1559
+ },
1560
+ {
1561
+ "id": "aggregate.ordered-set",
1562
+ "status": "unsupported",
1563
+ "example": "SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY amount) AS median FROM rows",
1564
+ "error": "Unsupported function: percentile_cont",
1565
+ "notes": "Ordered-set aggregates (percentile_cont, percentile_disc, mode) and corr are not supported; a window over ROW_NUMBER() and COUNT() OVER () computes a median."
1566
+ },
1357
1567
  {
1358
1568
  "id": "type.array",
1359
1569
  "status": "supported",
@@ -1388,6 +1598,41 @@
1388
1598
  "error": "PostgreSQL JSONB concatenation (||) is not supported",
1389
1599
  "notes": "PostgreSQL's jsonb || jsonb merges documents structurally. Minnow refuses || whenever either operand is JSONB rather than inventing a text concatenation PostgreSQL does not have."
1390
1600
  },
1601
+ {
1602
+ "id": "json.object-agg",
1603
+ "status": "unsupported",
1604
+ "example": "SELECT json_object_agg(region, amount) AS doc FROM rows",
1605
+ "error": "Unsupported function: json_object_agg",
1606
+ "notes": "json_object_agg / jsonb_object_agg are not supported; json_agg of json_build_object pairs is the usual substitute."
1607
+ },
1608
+ {
1609
+ "id": "json.mutation-functions",
1610
+ "status": "unsupported",
1611
+ "example": "SELECT jsonb_set('{\"a\":1}'::jsonb, '{a}', '2') AS doc FROM rows",
1612
+ "error": "Unsupported function: jsonb_set",
1613
+ "notes": "jsonb_set, jsonb_insert, jsonb_strip_nulls, and json_extract_path_text are not supported; rebuild the document with JSON_OBJECT / json_build_object and read members with -> and ->>."
1614
+ },
1615
+ {
1616
+ "id": "json.inspection-functions",
1617
+ "status": "unsupported",
1618
+ "example": "SELECT jsonb_typeof('{\"a\":1}'::jsonb) AS kind FROM rows",
1619
+ "error": "Unsupported function: jsonb_typeof",
1620
+ "notes": "jsonb_typeof and jsonb_array_length are not supported; JSON_EXISTS, JSON_VALUE, and JSON_QUERY answer most of the same questions."
1621
+ },
1622
+ {
1623
+ "id": "json.path-operators",
1624
+ "status": "unsupported",
1625
+ "example": "SELECT ('{\"a\":{\"b\":[1,2]}}'::jsonb) #>> '{a,b,0}' AS leaf FROM rows",
1626
+ "error": "Unsupported SQL character: #",
1627
+ "notes": "The #> and #>> path operators are not supported; chain -> and ->> or use JSON_VALUE with a path."
1628
+ },
1629
+ {
1630
+ "id": "json.containment-operators",
1631
+ "status": "unsupported",
1632
+ "example": "SELECT region FROM rows WHERE ('{\"theme\":\"dark\"}'::jsonb) @> '{\"theme\":\"dark\"}'",
1633
+ "error": "Unsupported SQL character: @",
1634
+ "notes": "The @>, <@, ?, ?|, and ?& operators are not supported; compare members with ->> or test presence with JSON_EXISTS."
1635
+ },
1391
1636
  {
1392
1637
  "id": "type.time",
1393
1638
  "status": "supported",
@@ -1400,6 +1645,13 @@
1400
1645
  "example": "SELECT CAST('2026-08-26' AS DATE) AS day",
1401
1646
  "notes": "A calendar date without a time zone, returned as canonical YYYY-MM-DD text."
1402
1647
  },
1648
+ {
1649
+ "id": "type.array-any-parameter",
1650
+ "status": "unsupported",
1651
+ "example": "SELECT region FROM rows WHERE amount = ANY(ARRAY[1, 2])",
1652
+ "error": "ANY/ALL take a subquery",
1653
+ "notes": "= ANY(array) is not supported; use IN (…) with a list or a subquery."
1654
+ },
1403
1655
  {
1404
1656
  "id": "ddl.sequence",
1405
1657
  "status": "supported",
@@ -1491,6 +1743,41 @@
1491
1743
  "example": "SELECT 2 ^ 10 AS kib, 2 ^ 3 ^ 2 AS left_assoc, -2 ^ 2 AS negated, 2 * 3 ^ 2 AS mixed",
1492
1744
  "notes": "PostgreSQL's ^ is exponentiation, binding above * and / and associating to the left: 2 ^ 3 ^ 2 is 64."
1493
1745
  },
1746
+ {
1747
+ "id": "expression.interval-values",
1748
+ "status": "unsupported",
1749
+ "example": "SELECT INTERVAL '1 day' + INTERVAL '2 hours' AS total FROM rows",
1750
+ "error": "Date arithmetic requires a date or datetime value",
1751
+ "notes": "Interval-valued arithmetic — interval + interval, timestamp - timestamp, date - date, date + integer, EXTRACT(EPOCH FROM interval), justify_days — is not supported. A date or datetime plus or minus an INTERVAL is; subtract two EXTRACT(EPOCH …) readings for a duration in seconds."
1752
+ },
1753
+ {
1754
+ "id": "expression.date-minus-date",
1755
+ "status": "unsupported",
1756
+ "example": "SELECT DATE '2026-01-10' - DATE '2026-01-05' AS days FROM rows",
1757
+ "error": "Arithmetic and numeric aggregates require numbers",
1758
+ "notes": "date - date and timestamp - timestamp are not supported; see expression.interval-values."
1759
+ },
1760
+ {
1761
+ "id": "expression.at-time-zone",
1762
+ "status": "unsupported",
1763
+ "example": "SELECT CURRENT_TIMESTAMP AT TIME ZONE 'UTC' AS local FROM rows",
1764
+ "error": "Expected eof, found AT",
1765
+ "notes": "AT TIME ZONE and timezone() are not supported: every datetime is an instant in UTC, and rendering in another zone belongs to the application."
1766
+ },
1767
+ {
1768
+ "id": "expression.bitwise-operators",
1769
+ "status": "unsupported",
1770
+ "example": "SELECT 1 & 3 AS both, 1 | 2 AS either, 1 # 3 AS differ, 1 << 2 AS shifted FROM rows",
1771
+ "error": "Unsupported SQL character: &",
1772
+ "notes": "The bitwise operators &, |, #, ~, <<, and >> and the prefix operators |/ and @ are not supported."
1773
+ },
1774
+ {
1775
+ "id": "expression.row-constructor-value",
1776
+ "status": "unsupported",
1777
+ "example": "SELECT ROW(1, 2) AS pair FROM rows",
1778
+ "error": "Unsupported function: ROW",
1779
+ "notes": "A row constructor as a value is not supported; row comparisons ((a, b) = (1, 2), (a, b) IN (…)) are."
1780
+ },
1494
1781
  {
1495
1782
  "id": "function.math-extended",
1496
1783
  "status": "supported",
@@ -1539,6 +1826,34 @@
1539
1826
  "example": "SELECT DATE_PART('year', joined) AS y, EXTRACT(DOY FROM joined) AS doy, EXTRACT(ISODOW FROM joined) AS isodow, EXTRACT(ISOYEAR FROM joined) AS isoyear, EXTRACT(DECADE FROM joined) AS decade, EXTRACT(MILLISECONDS FROM joined) AS ms, EXTRACT(YEAR FROM DATE '2026-03-04') AS from_date FROM rows WHERE joined IS NOT NULL",
1540
1827
  "notes": "DATE_PART('field', value) is EXTRACT(field FROM value). The fields year, quarter, month, week, day, hour, minute, second, epoch, dow, doy, isodow, isoyear, decade, century, millennium, milliseconds, and microseconds, over timestamps and DATE values, in UTC."
1541
1828
  },
1829
+ {
1830
+ "id": "function.unnest",
1831
+ "status": "unsupported",
1832
+ "example": "SELECT x FROM unnest(ARRAY[3, 1, 2]) AS x",
1833
+ "error": "Expected )",
1834
+ "notes": "Set-returning functions (unnest, generate_series, regexp_split_to_table, jsonb_array_elements) are not supported as FROM sources; use a VALUES source, a recursive CTE for a series, or JSON_TABLE for a JSON array."
1835
+ },
1836
+ {
1837
+ "id": "function.generate-series",
1838
+ "status": "unsupported",
1839
+ "example": "SELECT n FROM generate_series(1, 3) AS n",
1840
+ "error": "Expected identifier, found 1",
1841
+ "notes": "generate_series is not supported; a recursive CTE produces a series."
1842
+ },
1843
+ {
1844
+ "id": "function.regexp-arrays",
1845
+ "status": "unsupported",
1846
+ "example": "SELECT regexp_match(region, '(e.)') AS groups FROM rows",
1847
+ "error": "Unsupported function: regexp_match",
1848
+ "notes": "regexp_match, regexp_matches, regexp_split_to_array, and regexp_split_to_table return arrays or sets, which are not supported; SUBSTRING(text FROM 'pattern'), REGEXP_REPLACE, and the ~ operators cover single matches."
1849
+ },
1850
+ {
1851
+ "id": "function.server-introspection",
1852
+ "status": "unsupported",
1853
+ "example": "SELECT version() AS server FROM rows",
1854
+ "error": "Unsupported function: version",
1855
+ "notes": "version(), current_user, session_user, current_schema, pg_typeof, and setseed describe a server this engine is not; SHOW server_version answers the version question."
1856
+ },
1542
1857
  {
1543
1858
  "id": "group-by.ordinal",
1544
1859
  "status": "supported",
@@ -1551,6 +1866,12 @@
1551
1866
  "example": "SELECT COALESCE(region, 'none') AS place, COUNT(*) AS c FROM rows GROUP BY place ORDER BY place",
1552
1867
  "notes": "A bare GROUP BY name that is an output alias stands for the aliased expression. A name that is both an alias and a source column keeps its source-column meaning, which is also what PostgreSQL does."
1553
1868
  },
1869
+ {
1870
+ "id": "group-by.qualified-spelling",
1871
+ "status": "supported",
1872
+ "example": "SELECT region, SUM(r.amount) AS total FROM rows r GROUP BY r.region ORDER BY region",
1873
+ "notes": "A grouped column may be spelled bare in the select list and qualified in GROUP BY, or the reverse, and both spellings may appear in one expression. With several sources a bare name is the column of the one source that has it, resolved from the schemas at execution."
1874
+ },
1554
1875
  {
1555
1876
  "id": "group-by.expression-over-key",
1556
1877
  "status": "supported",
@@ -1569,6 +1890,12 @@
1569
1890
  "example": "SELECT region FROM rows ORDER BY amount LIMIT 0",
1570
1891
  "notes": "LIMIT 0 returns no rows and still reports the result's column shape, the way clients read a query's columns without fetching data."
1571
1892
  },
1893
+ {
1894
+ "id": "limit.all",
1895
+ "status": "supported",
1896
+ "example": "SELECT amount FROM rows ORDER BY amount LIMIT ALL OFFSET 1",
1897
+ "notes": "LIMIT ALL is PostgreSQL's spelling of no limit, alone or before OFFSET, on a block or a set operation. SQLite has no LIMIT ALL."
1898
+ },
1572
1899
  {
1573
1900
  "id": "set.trailing-order",
1574
1901
  "status": "supported",
@@ -1605,6 +1932,13 @@
1605
1932
  "example": "SELECT DISTINCT active, COUNT(*) AS c FROM rows GROUP BY region, active ORDER BY active, c",
1606
1933
  "notes": "DISTINCT over a grouped, aggregated, HAVING-filtered, or windowed block takes the distinct rows of that block's output, which runs inside a derived table; ORDER BY and LIMIT apply to the distinct rows."
1607
1934
  },
1935
+ {
1936
+ "id": "select.tablesample",
1937
+ "status": "unsupported",
1938
+ "example": "SELECT region FROM rows TABLESAMPLE SYSTEM (50)",
1939
+ "error": "Expected eof, found SYSTEM",
1940
+ "notes": "TABLESAMPLE is not supported; ORDER BY RANDOM() LIMIT n samples rows."
1941
+ },
1608
1942
  {
1609
1943
  "id": "mutation.update-alias",
1610
1944
  "status": "supported",
@@ -1641,6 +1975,34 @@
1641
1975
  "example": "INSERT INTO keyed (name, score) WITH top AS (SELECT name FROM keyed WHERE score > 0) SELECT name || '2', 2 FROM top UNION ALL SELECT 'u', 3",
1642
1976
  "notes": "Any query expression feeds INSERT: a WITH, a set operation, or a parenthesized member; the produced column count is checked against the column list when the rows materialize."
1643
1977
  },
1978
+ {
1979
+ "id": "mutation.upsert-non-key-unique",
1980
+ "status": "unsupported",
1981
+ "example": "INSERT INTO keyed (name, score) VALUES ('z', 3) ON CONFLICT (score) DO NOTHING",
1982
+ "error": "ON CONFLICT targets the table's primary or unique key columns: name",
1983
+ "notes": "ON CONFLICT targets the table's primary or row-addressing unique key; a secondary UNIQUE column, a constraint name (ON CONSTRAINT), or a partial-index predicate cannot be the target."
1984
+ },
1985
+ {
1986
+ "id": "mutation.update-set-default",
1987
+ "status": "unsupported",
1988
+ "example": "UPDATE keyed SET bonus = DEFAULT WHERE name = 'x'",
1989
+ "error": "Ambiguous or missing column: DEFAULT",
1990
+ "notes": "SET column = DEFAULT and the row-value form SET (a, b) = (…) are not supported; assign the value explicitly."
1991
+ },
1992
+ {
1993
+ "id": "mutation.update-row-value",
1994
+ "status": "unsupported",
1995
+ "example": "UPDATE keyed SET (score, bonus) = (1, 2) WHERE name = 'x'",
1996
+ "error": "Expected identifier, found (",
1997
+ "notes": "The row-value assignment SET (a, b) = (…) is not supported; assign each column."
1998
+ },
1999
+ {
2000
+ "id": "mutation.data-modifying-cte",
2001
+ "status": "unsupported",
2002
+ "example": "WITH w AS (INSERT INTO keyed (name, score) VALUES ('w', 0) RETURNING name) SELECT name FROM w",
2003
+ "error": "Expected SELECT, found INSERT",
2004
+ "notes": "A data-modifying statement inside WITH is not supported; run the write, then the read."
2005
+ },
1644
2006
  {
1645
2007
  "id": "ddl.serial",
1646
2008
  "status": "supported",
@@ -1677,6 +2039,181 @@
1677
2039
  "example": "CREATE TABLE default_children (id INTEGER PRIMARY KEY, parent INTEGER REFERENCES default_parents(id) ON DELETE SET DEFAULT)",
1678
2040
  "error": "SET DEFAULT is not supported; use SET NULL or CASCADE",
1679
2041
  "notes": "SET DEFAULT would rewrite orphaned references to the column's default value at delete time; Minnow implements RESTRICT, CASCADE, and SET NULL. The statement is rejected at parse, before touching the catalog."
2042
+ },
2043
+ {
2044
+ "id": "ddl.alter-table-rename",
2045
+ "status": "unsupported",
2046
+ "example": "ALTER TABLE keyed RENAME COLUMN score TO points",
2047
+ "error": "Expected ADD, found RENAME",
2048
+ "notes": "ALTER TABLE RENAME COLUMN, RENAME TO, ALTER COLUMN TYPE / SET DEFAULT / SET NOT NULL / DROP NOT NULL, ADD CONSTRAINT, and DROP CONSTRAINT are not supported as SQL. ALTER TABLE ADD COLUMN and DROP COLUMN are. The schema DSL's migrate() renames columns and widens nullability through the catalog."
2049
+ },
2050
+ {
2051
+ "id": "ddl.alter-column",
2052
+ "status": "unsupported",
2053
+ "example": "ALTER TABLE keyed ALTER COLUMN score SET DEFAULT 7",
2054
+ "error": "Expected ADD, found ALTER",
2055
+ "notes": "ALTER COLUMN forms are not supported; see ddl.alter-table-rename."
2056
+ },
2057
+ {
2058
+ "id": "ddl.add-constraint",
2059
+ "status": "unsupported",
2060
+ "example": "ALTER TABLE keyed ADD CONSTRAINT keyed_score_positive CHECK (score > -10)",
2061
+ "error": "Expected eof, found CHECK",
2062
+ "notes": "ADD CONSTRAINT and DROP CONSTRAINT are not supported; constraints are declared in CREATE TABLE."
2063
+ },
2064
+ {
2065
+ "id": "ddl.add-column-if-not-exists",
2066
+ "status": "unsupported",
2067
+ "example": "ALTER TABLE keyed ADD COLUMN IF NOT EXISTS note TEXT",
2068
+ "error": "Expected eof, found EXISTS",
2069
+ "notes": "ADD COLUMN IF NOT EXISTS is not supported; check the catalog first or let the duplicate-column error stand."
2070
+ },
2071
+ {
2072
+ "id": "ddl.create-table-like",
2073
+ "status": "unsupported",
2074
+ "example": "CREATE TABLE copied (LIKE keyed)",
2075
+ "error": "Unsupported column type: keyed",
2076
+ "notes": "CREATE TABLE … (LIKE other) is not supported; spell the columns out, or CREATE TABLE AS SELECT for a data copy."
2077
+ },
2078
+ {
2079
+ "id": "ddl.serial-non-key",
2080
+ "status": "unsupported",
2081
+ "example": "CREATE TABLE ticketed (id INTEGER PRIMARY KEY, seq SERIAL)",
2082
+ "error": "Auto-increment requires the unique key column: seq",
2083
+ "notes": "SERIAL and GENERATED … AS IDENTITY are supported on the primary key only; a sequence-fed non-key column is refused."
2084
+ },
2085
+ {
2086
+ "id": "ddl.bytea",
2087
+ "status": "unsupported",
2088
+ "example": "CREATE TABLE blobs (id INTEGER PRIMARY KEY, body BYTEA)",
2089
+ "error": "Unsupported column type: BYTEA",
2090
+ "notes": "BYTEA columns and bytea literals, casts, and functions (encode, decode, sha256) are not supported; store binary data as base64 or hex TEXT."
2091
+ },
2092
+ {
2093
+ "id": "ddl.foreign-key-on-update",
2094
+ "status": "unsupported",
2095
+ "example": "CREATE TABLE child (id INTEGER PRIMARY KEY, parent TEXT REFERENCES keyed (name) ON UPDATE CASCADE)",
2096
+ "error": "ON UPDATE CASCADE has nothing to act on: a unique key cannot change",
2097
+ "notes": "ON UPDATE actions are not supported because primary-key values cannot be updated; ON DELETE CASCADE, SET NULL, RESTRICT, and NO ACTION are."
2098
+ },
2099
+ {
2100
+ "id": "ddl.deferrable-constraints",
2101
+ "status": "unsupported",
2102
+ "example": "CREATE TABLE deferred (id INTEGER PRIMARY KEY, parent TEXT REFERENCES keyed (name) DEFERRABLE INITIALLY DEFERRED)",
2103
+ "error": "Expected )",
2104
+ "notes": "DEFERRABLE constraints are not supported; every constraint is checked at the statement."
2105
+ },
2106
+ {
2107
+ "id": "ddl.partial-index",
2108
+ "status": "unsupported",
2109
+ "example": "CREATE INDEX keyed_positive ON keyed (score) WHERE score > 0",
2110
+ "error": "Expected eof, found WHERE",
2111
+ "notes": "Partial indexes (WHERE), expression indexes, INCLUDE columns, USING method, and CONCURRENTLY are not supported; an index is over one or more plain columns."
2112
+ },
2113
+ {
2114
+ "id": "ddl.expression-index",
2115
+ "status": "unsupported",
2116
+ "example": "CREATE INDEX keyed_lower ON keyed (LOWER(name))",
2117
+ "error": "Expected )",
2118
+ "notes": "Expression indexes are not supported; index a stored generated column instead."
2119
+ },
2120
+ {
2121
+ "id": "ddl.drop-table-multiple",
2122
+ "status": "unsupported",
2123
+ "example": "DROP TABLE IF EXISTS keyed, rows",
2124
+ "error": "Expected eof, found ,",
2125
+ "notes": "DROP TABLE takes one table per statement."
2126
+ },
2127
+ {
2128
+ "id": "ddl.view-column-list",
2129
+ "status": "unsupported",
2130
+ "example": "CREATE VIEW scored (person, points) AS SELECT name, score FROM keyed",
2131
+ "error": "CREATE VIEW takes a name and AS <query>; column lists are not supported",
2132
+ "notes": "A column list on CREATE VIEW and WITH CHECK OPTION are not supported; alias the columns in the view's SELECT."
2133
+ },
2134
+ {
2135
+ "id": "ddl.materialized-view",
2136
+ "status": "unsupported",
2137
+ "example": "CREATE MATERIALIZED VIEW scores AS SELECT name, score FROM keyed",
2138
+ "error": "Expected TABLE, found MATERIALIZED",
2139
+ "notes": "Materialized views are not supported; CREATE TABLE AS SELECT stores a snapshot."
2140
+ },
2141
+ {
2142
+ "id": "ddl.sequence-functions",
2143
+ "status": "unsupported",
2144
+ "example": "SELECT setval('numbering', 10) FROM rows",
2145
+ "error": "Unsupported function: setval",
2146
+ "notes": "setval, currval, and ALTER SEQUENCE are not supported; nextval and CREATE SEQUENCE are."
2147
+ },
2148
+ {
2149
+ "id": "ddl.alter-type",
2150
+ "status": "unsupported",
2151
+ "example": "ALTER TYPE mood ADD VALUE 'meh'",
2152
+ "error": "Expected TABLE, found TYPE",
2153
+ "notes": "ALTER TYPE and DROP TYPE are not supported; CREATE TYPE … AS ENUM is, and the schema DSL widens enum values through migrate()."
2154
+ },
2155
+ {
2156
+ "id": "ddl.domain",
2157
+ "status": "unsupported",
2158
+ "example": "CREATE DOMAIN positive_int AS INTEGER CHECK (VALUE > 0)",
2159
+ "error": "Expected TABLE, found DOMAIN",
2160
+ "notes": "CREATE DOMAIN is not supported; put the CHECK on the column."
2161
+ },
2162
+ {
2163
+ "id": "ddl.schema",
2164
+ "status": "unsupported",
2165
+ "example": "CREATE SCHEMA app",
2166
+ "error": "Expected TABLE, found SCHEMA",
2167
+ "notes": "Schemas are not supported: there is one namespace, and a schema-qualified name (public.users) is refused."
2168
+ },
2169
+ {
2170
+ "id": "ddl.schema-qualified-name",
2171
+ "status": "unsupported",
2172
+ "example": "SELECT region FROM public.rows",
2173
+ "error": "Expected eof, found .",
2174
+ "notes": "Schema-qualified names are not supported; there is one namespace."
2175
+ },
2176
+ {
2177
+ "id": "ddl.function",
2178
+ "status": "unsupported",
2179
+ "example": "CREATE FUNCTION one() RETURNS integer AS $$ SELECT 1 $$ LANGUAGE sql",
2180
+ "error": "Expected TABLE, found FUNCTION",
2181
+ "notes": "CREATE FUNCTION and CREATE TRIGGER … EXECUTE FUNCTION are not supported; triggers take an inline BEGIN … END body of INSERT, UPDATE, and DELETE statements."
2182
+ },
2183
+ {
2184
+ "id": "ddl.exclude-constraint",
2185
+ "status": "unsupported",
2186
+ "example": "CREATE TABLE slots (id INTEGER PRIMARY KEY, n INTEGER, EXCLUDE USING gist (n WITH =))",
2187
+ "error": "Expected )",
2188
+ "notes": "EXCLUDE constraints are not supported."
2189
+ },
2190
+ {
2191
+ "id": "statement.server-commands",
2192
+ "status": "unsupported",
2193
+ "example": "VACUUM",
2194
+ "error": "Expected SELECT, found VACUUM",
2195
+ "notes": "VACUUM, ANALYZE, COMMENT ON, LISTEN, NOTIFY, and DISCARD are server commands with no meaning here; maintenance runs automatically and is driven from the API."
2196
+ },
2197
+ {
2198
+ "id": "statement.multiple",
2199
+ "status": "unsupported",
2200
+ "example": "SELECT 1 AS a; SELECT 2 AS b",
2201
+ "error": "Run one SELECT statement at a time",
2202
+ "notes": "One statement per execute() call; a script is split at its semicolons by the caller."
2203
+ },
2204
+ {
2205
+ "id": "statement.explain",
2206
+ "status": "unsupported",
2207
+ "example": "EXPLAIN SELECT region FROM rows",
2208
+ "error": "Expected SELECT, found EXPLAIN",
2209
+ "notes": "EXPLAIN is not supported as SQL; the explain() API renders the plan for a query."
2210
+ },
2211
+ {
2212
+ "id": "catalog.information-schema",
2213
+ "status": "unsupported",
2214
+ "example": "SELECT column_name FROM information_schema.columns WHERE table_name = 'rows'",
2215
+ "error": "Expected eof, found .",
2216
+ "notes": "information_schema, pg_catalog, and sqlite_master are not supported; the catalog is read through the API (listTables, describe)."
1680
2217
  }
1681
2218
  ]
1682
2219
  }