@minnowdb/core 0.2.1 → 0.3.0
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.
- package/README.md +21 -32
- package/dist/engine/artifact-cache.d.ts +2 -0
- package/dist/engine/artifact-cache.d.ts.map +1 -1
- package/dist/engine/artifact-cache.js +5 -0
- package/dist/engine/artifact-cache.js.map +1 -1
- package/dist/engine/batch.d.ts +6 -1
- package/dist/engine/batch.d.ts.map +1 -1
- package/dist/engine/batch.js +17 -6
- package/dist/engine/batch.js.map +1 -1
- package/dist/engine/catalog.d.ts +22 -2
- package/dist/engine/catalog.d.ts.map +1 -1
- package/dist/engine/catalog.js +30 -3
- package/dist/engine/catalog.js.map +1 -1
- package/dist/engine/client.d.ts +2 -0
- package/dist/engine/client.d.ts.map +1 -1
- package/dist/engine/client.js +20 -10
- package/dist/engine/client.js.map +1 -1
- package/dist/engine/database.d.ts +67 -5
- package/dist/engine/database.d.ts.map +1 -1
- package/dist/engine/database.js +3508 -369
- package/dist/engine/database.js.map +1 -1
- package/dist/engine/defaults.d.ts +4 -7
- package/dist/engine/defaults.d.ts.map +1 -1
- package/dist/engine/defaults.js +47 -21
- package/dist/engine/defaults.js.map +1 -1
- package/dist/engine/fts.d.ts.map +1 -1
- package/dist/engine/fts.js +2 -0
- package/dist/engine/fts.js.map +1 -1
- package/dist/engine/index.d.ts +1 -0
- package/dist/engine/index.d.ts.map +1 -1
- package/dist/engine/index.js +1 -0
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/live.d.ts.map +1 -1
- package/dist/engine/live.js +10 -2
- package/dist/engine/live.js.map +1 -1
- package/dist/engine/optimizer.d.ts.map +1 -1
- package/dist/engine/optimizer.js +258 -23
- package/dist/engine/optimizer.js.map +1 -1
- package/dist/engine/query-cache.d.ts +1 -1
- package/dist/engine/query-cache.d.ts.map +1 -1
- package/dist/engine/query-cache.js +3 -1
- package/dist/engine/query-cache.js.map +1 -1
- package/dist/engine/query.d.ts +130 -22
- package/dist/engine/query.d.ts.map +1 -1
- package/dist/engine/query.js +1344 -241
- package/dist/engine/query.js.map +1 -1
- package/dist/engine/schema-wire.d.ts +6 -2
- package/dist/engine/schema-wire.d.ts.map +1 -1
- package/dist/engine/schema-wire.js +40 -33
- package/dist/engine/schema-wire.js.map +1 -1
- package/dist/engine/schema.d.ts +157 -76
- package/dist/engine/schema.d.ts.map +1 -1
- package/dist/engine/schema.js +548 -103
- package/dist/engine/schema.js.map +1 -1
- package/dist/engine/sort-keys.d.ts +4 -3
- package/dist/engine/sort-keys.d.ts.map +1 -1
- package/dist/engine/sort-keys.js +42 -27
- package/dist/engine/sort-keys.js.map +1 -1
- package/dist/engine/sql-domains.d.ts +26 -0
- package/dist/engine/sql-domains.d.ts.map +1 -0
- package/dist/engine/sql-domains.js +421 -0
- package/dist/engine/sql-domains.js.map +1 -0
- package/dist/engine/sql-driver.d.ts +19 -0
- package/dist/engine/sql-driver.d.ts.map +1 -0
- package/dist/engine/sql-driver.js +2 -0
- package/dist/engine/sql-driver.js.map +1 -0
- package/dist/engine/sql-json.d.ts +7 -8
- package/dist/engine/sql-json.d.ts.map +1 -1
- package/dist/engine/sql-json.js +28 -14
- package/dist/engine/sql-json.js.map +1 -1
- package/dist/engine/sql-semantics.d.ts +2 -0
- package/dist/engine/sql-semantics.d.ts.map +1 -1
- package/dist/engine/sql-semantics.js +69 -3
- package/dist/engine/sql-semantics.js.map +1 -1
- package/dist/engine/vector.d.ts +5 -1
- package/dist/engine/vector.d.ts.map +1 -1
- package/dist/engine/vector.js +433 -61
- package/dist/engine/vector.js.map +1 -1
- package/dist/engine/worker-host.d.ts +1 -0
- package/dist/engine/worker-host.d.ts.map +1 -1
- package/dist/engine/worker-host.js +4 -0
- package/dist/engine/worker-host.js.map +1 -1
- package/dist/plan/index.d.ts +3 -3
- package/dist/plan/index.js +3 -3
- package/dist/storage/indexeddb.d.ts +44 -5
- package/dist/storage/indexeddb.d.ts.map +1 -1
- package/dist/storage/indexeddb.js +738 -175
- package/dist/storage/indexeddb.js.map +1 -1
- package/dist/storage/memory.d.ts +27 -10
- package/dist/storage/memory.d.ts.map +1 -1
- package/dist/storage/memory.js +67 -18
- package/dist/storage/memory.js.map +1 -1
- package/dist/storage/opfs/leader.d.ts +28 -6
- package/dist/storage/opfs/leader.d.ts.map +1 -1
- package/dist/storage/opfs/leader.js +301 -34
- package/dist/storage/opfs/leader.js.map +1 -1
- package/dist/storage/opfs/rpc.d.ts.map +1 -1
- package/dist/storage/opfs/rpc.js +2 -1
- package/dist/storage/opfs/rpc.js.map +1 -1
- package/dist/storage/opfs/store.d.ts +34 -5
- package/dist/storage/opfs/store.d.ts.map +1 -1
- package/dist/storage/opfs/store.js +41 -0
- package/dist/storage/opfs/store.js.map +1 -1
- package/dist/storage/snapshot.d.ts +14 -0
- package/dist/storage/snapshot.d.ts.map +1 -1
- package/dist/storage/snapshot.js +44 -0
- package/dist/storage/snapshot.js.map +1 -1
- package/dist/storage/toolkit/index.d.ts +1 -1
- package/dist/storage/toolkit/index.d.ts.map +1 -1
- package/dist/storage/toolkit/index.js +1 -1
- package/dist/storage/toolkit/index.js.map +1 -1
- package/dist/storage/toolkit/record-core.d.ts +19 -5
- package/dist/storage/toolkit/record-core.d.ts.map +1 -1
- package/dist/storage/toolkit/record-core.js +353 -78
- package/dist/storage/toolkit/record-core.js.map +1 -1
- package/dist/storage/toolkit/wire.d.ts +15 -0
- package/dist/storage/toolkit/wire.d.ts.map +1 -1
- package/dist/storage/toolkit/wire.js +134 -0
- package/dist/storage/toolkit/wire.js.map +1 -1
- package/dist/storage/types.d.ts +213 -31
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/types.js +291 -20
- package/dist/storage/types.js.map +1 -1
- package/dist/testing/block-store-conformance.d.ts.map +1 -1
- package/dist/testing/block-store-conformance.js +153 -2
- package/dist/testing/block-store-conformance.js.map +1 -1
- package/dist/testing/index.d.ts +9 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +23 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/simulator.d.ts +98 -0
- package/dist/testing/simulator.d.ts.map +1 -0
- package/dist/testing/simulator.js +560 -0
- package/dist/testing/simulator.js.map +1 -0
- package/dist/testing/sqllogictest.d.ts +90 -0
- package/dist/testing/sqllogictest.d.ts.map +1 -0
- package/dist/testing/sqllogictest.js +435 -0
- package/dist/testing/sqllogictest.js.map +1 -0
- package/dist/transactions/index.d.ts +18 -3
- package/dist/transactions/index.d.ts.map +1 -1
- package/dist/transactions/index.js +64 -8
- package/dist/transactions/index.js.map +1 -1
- package/package.json +5 -3
- package/postgres-feature-profile.json +223 -0
- package/sql-feature-matrix.json +172 -252
package/sql-feature-matrix.json
CHANGED
|
@@ -1,90 +1,76 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version":
|
|
3
|
-
"description": "
|
|
2
|
+
"version": 4,
|
|
3
|
+
"description": "Executable PostgreSQL compatibility examples for MinnowDatabase. postgres-feature-profile.json records differences, extensions, and embedded exclusions.",
|
|
4
4
|
"features": [
|
|
5
5
|
{
|
|
6
6
|
"id": "select.projection",
|
|
7
|
-
"feature": "E051",
|
|
8
7
|
"status": "supported",
|
|
9
8
|
"example": "SELECT region, amount FROM rows"
|
|
10
9
|
},
|
|
11
10
|
{
|
|
12
11
|
"id": "select.alias",
|
|
13
|
-
"feature": "E051-05",
|
|
14
12
|
"status": "supported",
|
|
15
13
|
"example": "SELECT amount AS total FROM rows"
|
|
16
14
|
},
|
|
17
15
|
{
|
|
18
16
|
"id": "select.wildcard",
|
|
19
|
-
"feature": "E051",
|
|
20
17
|
"status": "supported",
|
|
21
18
|
"example": "SELECT * FROM rows"
|
|
22
19
|
},
|
|
23
20
|
{
|
|
24
21
|
"id": "select.distinct",
|
|
25
|
-
"feature": "E051-01",
|
|
26
22
|
"status": "supported",
|
|
27
23
|
"example": "SELECT DISTINCT region FROM rows"
|
|
28
24
|
},
|
|
29
25
|
{
|
|
30
26
|
"id": "select.scalar-subquery",
|
|
31
|
-
"feature": "F471",
|
|
32
27
|
"status": "supported",
|
|
33
28
|
"example": "SELECT (SELECT MAX(amount) FROM rows) AS peak FROM rows LIMIT 1"
|
|
34
29
|
},
|
|
35
30
|
{
|
|
36
31
|
"id": "expression.arithmetic",
|
|
37
|
-
"feature": "E011-04",
|
|
38
32
|
"status": "supported",
|
|
39
33
|
"example": "SELECT amount * 2 + 1 AS scaled FROM rows"
|
|
40
34
|
},
|
|
41
35
|
{
|
|
42
36
|
"id": "expression.round",
|
|
43
|
-
"feature": "T441",
|
|
44
37
|
"status": "supported",
|
|
45
38
|
"example": "SELECT ROUND(amount / 3, 2) AS thirds FROM rows",
|
|
46
39
|
"notes": "Precision truncates to an integer and clamps to 0..30; halfway values round away from zero, matching SQLite."
|
|
47
40
|
},
|
|
48
41
|
{
|
|
49
42
|
"id": "literal.string",
|
|
50
|
-
"feature": "E021-03",
|
|
51
43
|
"status": "supported",
|
|
52
44
|
"example": "SELECT region FROM rows WHERE region = 'west'"
|
|
53
45
|
},
|
|
54
46
|
{
|
|
55
47
|
"id": "literal.number",
|
|
56
|
-
"feature": "E011",
|
|
57
48
|
"status": "supported",
|
|
58
49
|
"example": "SELECT region FROM rows WHERE amount >= 10"
|
|
59
50
|
},
|
|
60
51
|
{
|
|
61
52
|
"id": "literal.boolean",
|
|
62
|
-
"feature": "T031",
|
|
63
53
|
"status": "supported",
|
|
64
54
|
"example": "SELECT active, amount FROM rows WHERE active = TRUE"
|
|
65
55
|
},
|
|
66
56
|
{
|
|
67
57
|
"id": "literal.null-comparison",
|
|
68
|
-
"feature": "E131",
|
|
69
58
|
"status": "supported",
|
|
70
59
|
"example": "SELECT region FROM rows WHERE region != NULL"
|
|
71
60
|
},
|
|
72
61
|
{
|
|
73
62
|
"id": "literal.date",
|
|
74
|
-
"feature": "F051-01",
|
|
75
63
|
"status": "supported",
|
|
76
64
|
"example": "SELECT region FROM rows WHERE joined >= DATE '2026-01-01'"
|
|
77
65
|
},
|
|
78
66
|
{
|
|
79
67
|
"id": "literal.timestamp",
|
|
80
|
-
"feature": "F051-03",
|
|
81
68
|
"status": "supported",
|
|
82
69
|
"example": "SELECT region FROM rows WHERE joined >= TIMESTAMP '2026-01-01 00:00:00'",
|
|
83
70
|
"notes": "TIMESTAMP 'y-m-d h:m:s' with the time optional. A literal without a zone is UTC, as every datetime in a Minnow database is."
|
|
84
71
|
},
|
|
85
72
|
{
|
|
86
73
|
"id": "parameter.numbered",
|
|
87
|
-
"feature": "E182",
|
|
88
74
|
"status": "supported",
|
|
89
75
|
"example": "SELECT region, amount FROM rows WHERE amount >= $1 ORDER BY amount",
|
|
90
76
|
"params": [6],
|
|
@@ -92,7 +78,6 @@
|
|
|
92
78
|
},
|
|
93
79
|
{
|
|
94
80
|
"id": "parameter.positional",
|
|
95
|
-
"feature": "E182",
|
|
96
81
|
"status": "supported",
|
|
97
82
|
"example": "SELECT region, amount FROM rows WHERE amount >= ? AND active = ? ORDER BY amount",
|
|
98
83
|
"params": [6, true],
|
|
@@ -100,280 +85,257 @@
|
|
|
100
85
|
},
|
|
101
86
|
{
|
|
102
87
|
"id": "join.inner-equi",
|
|
103
|
-
"feature": "F041-01",
|
|
104
88
|
"status": "supported",
|
|
105
89
|
"example": "SELECT r.region, d.label FROM rows r JOIN dims d ON d.region = r.region"
|
|
106
90
|
},
|
|
107
91
|
{
|
|
108
92
|
"id": "join.left-equi",
|
|
109
|
-
"feature": "F041-03",
|
|
110
93
|
"status": "supported",
|
|
111
94
|
"example": "SELECT r.region, d.label FROM rows r LEFT JOIN dims d ON d.region = r.region"
|
|
112
95
|
},
|
|
113
96
|
{
|
|
114
97
|
"id": "where.and",
|
|
115
|
-
"feature": "E061-14",
|
|
116
98
|
"status": "supported",
|
|
117
99
|
"example": "SELECT region FROM rows WHERE amount > 5 AND region = 'west'"
|
|
118
100
|
},
|
|
119
101
|
{
|
|
120
102
|
"id": "where.in-list",
|
|
121
|
-
"feature": "E061-03",
|
|
122
103
|
"status": "supported",
|
|
123
104
|
"example": "SELECT region FROM rows WHERE region IN ('west', 'east')"
|
|
124
105
|
},
|
|
125
106
|
{
|
|
126
107
|
"id": "where.not-in-list",
|
|
127
|
-
"feature": "E061-03",
|
|
128
108
|
"status": "supported",
|
|
129
109
|
"example": "SELECT region FROM rows WHERE region NOT IN ('north')"
|
|
130
110
|
},
|
|
131
111
|
{
|
|
132
112
|
"id": "where.in-subquery",
|
|
133
|
-
"feature": "E061-11",
|
|
134
113
|
"status": "supported",
|
|
135
114
|
"example": "SELECT region FROM rows WHERE region IN (SELECT region FROM dims)"
|
|
136
115
|
},
|
|
137
116
|
{
|
|
138
117
|
"id": "where.scalar-subquery",
|
|
139
|
-
"feature": "E061-09",
|
|
140
118
|
"status": "supported",
|
|
141
119
|
"example": "SELECT region FROM rows WHERE amount > (SELECT AVG(amount) FROM rows)"
|
|
142
120
|
},
|
|
143
121
|
{
|
|
144
122
|
"id": "group-by",
|
|
145
|
-
"feature": "E051-02",
|
|
146
123
|
"status": "supported",
|
|
147
124
|
"example": "SELECT region, COUNT(*) AS count FROM rows GROUP BY region"
|
|
148
125
|
},
|
|
149
126
|
{
|
|
150
127
|
"id": "group-by.rollup",
|
|
151
|
-
"feature": "T431",
|
|
152
128
|
"status": "supported",
|
|
153
129
|
"example": "SELECT region, SUM(amount) AS total FROM rows GROUP BY ROLLUP(region)",
|
|
154
130
|
"notes": "ROLLUP/CUBE/GROUPING SETS desugar into a UNION ALL of grouped blocks. The GROUPING() marker is deliberately unsupported, so rollup NULLs and data NULLs are indistinguishable. SQLite itself has none of these."
|
|
155
131
|
},
|
|
156
132
|
{
|
|
157
133
|
"id": "group-by.grouping-sets",
|
|
158
|
-
"feature": "T431",
|
|
159
134
|
"status": "supported",
|
|
160
135
|
"example": "SELECT region, active, COUNT(*) AS c FROM rows GROUP BY GROUPING SETS ((region), (active), ())"
|
|
161
136
|
},
|
|
162
137
|
{
|
|
163
138
|
"id": "having",
|
|
164
|
-
"feature": "E051-06",
|
|
165
139
|
"status": "supported",
|
|
166
140
|
"example": "SELECT region, COUNT(*) AS count FROM rows GROUP BY region HAVING COUNT(*) > 1"
|
|
167
141
|
},
|
|
168
142
|
{
|
|
169
143
|
"id": "aggregate.count",
|
|
170
|
-
"feature": "E091-02",
|
|
171
144
|
"status": "supported",
|
|
172
145
|
"example": "SELECT COUNT(*) AS count FROM rows"
|
|
173
146
|
},
|
|
174
147
|
{
|
|
175
148
|
"id": "aggregate.sum",
|
|
176
|
-
"feature": "E091-05",
|
|
177
149
|
"status": "supported",
|
|
178
150
|
"example": "SELECT SUM(amount) AS total FROM rows"
|
|
179
151
|
},
|
|
180
152
|
{
|
|
181
153
|
"id": "aggregate.avg",
|
|
182
|
-
"feature": "E091-01",
|
|
183
154
|
"status": "supported",
|
|
184
155
|
"example": "SELECT AVG(amount) AS mean FROM rows"
|
|
185
156
|
},
|
|
186
157
|
{
|
|
187
158
|
"id": "aggregate.min-max",
|
|
188
|
-
"feature": "E091-03",
|
|
189
159
|
"status": "supported",
|
|
190
160
|
"example": "SELECT MIN(amount) AS low, MAX(amount) AS high FROM rows"
|
|
191
161
|
},
|
|
192
162
|
{
|
|
193
163
|
"id": "order-by.multi-column",
|
|
194
|
-
"feature": "E121",
|
|
195
164
|
"status": "supported",
|
|
196
165
|
"example": "SELECT region, amount FROM rows ORDER BY region, amount DESC"
|
|
197
166
|
},
|
|
198
167
|
{
|
|
199
168
|
"id": "order-by.wildcard-reference",
|
|
200
|
-
"feature": "E121",
|
|
201
169
|
"status": "supported",
|
|
202
170
|
"example": "SELECT * FROM rows ORDER BY amount"
|
|
203
171
|
},
|
|
204
172
|
{
|
|
205
173
|
"id": "limit",
|
|
206
|
-
"feature": "F856",
|
|
207
174
|
"status": "supported",
|
|
208
175
|
"example": "SELECT amount FROM rows ORDER BY amount LIMIT 2"
|
|
209
176
|
},
|
|
210
177
|
{
|
|
211
178
|
"id": "cte.non-recursive",
|
|
212
|
-
"feature": "T121",
|
|
213
179
|
"status": "supported",
|
|
214
180
|
"example": "WITH west AS (SELECT amount FROM rows WHERE region = 'west') SELECT COUNT(*) AS count FROM west"
|
|
215
181
|
},
|
|
216
182
|
{
|
|
217
183
|
"id": "cte.chained",
|
|
218
|
-
"feature": "T121",
|
|
219
184
|
"status": "supported",
|
|
220
185
|
"example": "WITH a AS (SELECT amount FROM rows), b AS (SELECT amount FROM a WHERE amount > 5) SELECT COUNT(*) AS count FROM b"
|
|
221
186
|
},
|
|
222
187
|
{
|
|
223
188
|
"id": "cte.column-list",
|
|
224
|
-
"feature": "T121",
|
|
225
189
|
"status": "supported",
|
|
226
190
|
"example": "WITH totals(place, total) AS (SELECT region, SUM(amount) FROM rows GROUP BY region) SELECT place, total FROM totals",
|
|
227
191
|
"notes": "A CTE names its own output columns. A recursive CTE takes the names before its step member, which refers to the working set by them."
|
|
228
192
|
},
|
|
229
193
|
{
|
|
230
194
|
"id": "derived-table",
|
|
231
|
-
"feature": "F591",
|
|
232
195
|
"status": "supported",
|
|
233
196
|
"example": "SELECT d.total FROM (SELECT region, SUM(amount) AS total FROM rows GROUP BY region) d ORDER BY d.total"
|
|
234
197
|
},
|
|
235
198
|
{
|
|
236
199
|
"id": "union.distinct",
|
|
237
|
-
"feature": "E071-01",
|
|
238
200
|
"status": "supported",
|
|
239
201
|
"example": "SELECT region FROM rows UNION SELECT region FROM dims ORDER BY region"
|
|
240
202
|
},
|
|
241
203
|
{
|
|
242
204
|
"id": "union.all",
|
|
243
|
-
"feature": "E071-02",
|
|
244
205
|
"status": "supported",
|
|
245
206
|
"example": "SELECT region FROM rows UNION ALL SELECT region FROM dims"
|
|
246
207
|
},
|
|
247
208
|
{
|
|
248
209
|
"id": "window.row-number",
|
|
249
|
-
"feature": "T611",
|
|
250
210
|
"status": "supported",
|
|
251
211
|
"example": "SELECT region, ROW_NUMBER() OVER (PARTITION BY region ORDER BY amount) AS rn FROM rows"
|
|
252
212
|
},
|
|
253
213
|
{
|
|
254
214
|
"id": "window.rank",
|
|
255
|
-
"feature": "T611",
|
|
256
215
|
"status": "supported",
|
|
257
216
|
"example": "SELECT region, RANK() OVER (ORDER BY amount) AS r FROM rows"
|
|
258
217
|
},
|
|
259
218
|
{
|
|
260
219
|
"id": "window.dense-rank",
|
|
261
|
-
"feature": "T611",
|
|
262
220
|
"status": "supported",
|
|
263
221
|
"example": "SELECT region, DENSE_RANK() OVER (ORDER BY amount) AS dr FROM rows"
|
|
264
222
|
},
|
|
265
223
|
{
|
|
266
224
|
"id": "mutation.insert-values",
|
|
267
|
-
"feature": "E101-01",
|
|
268
225
|
"status": "supported",
|
|
269
226
|
"example": "INSERT INTO keyed (name, score) VALUES ('a', 1), ('b', 2)",
|
|
270
227
|
"notes": "Through execute(); query() stays read-only."
|
|
271
228
|
},
|
|
229
|
+
{
|
|
230
|
+
"id": "mutation.insert-values-implicit-columns",
|
|
231
|
+
"status": "supported",
|
|
232
|
+
"example": "INSERT INTO keyed VALUES ('z', 2, NULL)",
|
|
233
|
+
"notes": "When the column list is omitted, VALUES must supply every table column in declaration order."
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"id": "mutation.insert-default-values",
|
|
237
|
+
"status": "supported",
|
|
238
|
+
"setup": [
|
|
239
|
+
"CREATE TABLE defaulted_insert (name TEXT DEFAULT 'generated', score INTEGER NOT NULL DEFAULT 7)"
|
|
240
|
+
],
|
|
241
|
+
"example": "INSERT INTO defaulted_insert DEFAULT VALUES",
|
|
242
|
+
"notes": "DEFAULT VALUES inserts one row using catalog defaults. DEFAULT is also accepted in an individual VALUES slot."
|
|
243
|
+
},
|
|
272
244
|
{
|
|
273
245
|
"id": "mutation.update-keyed",
|
|
274
|
-
"feature": "E101-03",
|
|
275
246
|
"status": "supported",
|
|
276
247
|
"example": "UPDATE keyed SET score = score + 1 WHERE score > 0",
|
|
277
248
|
"notes": "Requires a unique-key table; read-then-mutate, not serializable."
|
|
278
249
|
},
|
|
279
250
|
{
|
|
280
251
|
"id": "mutation.delete-keyed",
|
|
281
|
-
"feature": "E101-04",
|
|
282
252
|
"status": "supported",
|
|
283
253
|
"example": "DELETE FROM keyed WHERE score < 0",
|
|
284
254
|
"notes": "Requires a unique-key table."
|
|
285
255
|
},
|
|
286
256
|
{
|
|
287
257
|
"id": "mutation.returning",
|
|
288
|
-
"feature": "T495",
|
|
289
258
|
"status": "supported",
|
|
290
259
|
"example": "DELETE FROM keyed WHERE name = 'x' RETURNING name, score",
|
|
291
260
|
"notes": "RETURNING works on INSERT, UPDATE, and DELETE; inserts echo written values, updates return post-update values, deletes the rows as read."
|
|
292
261
|
},
|
|
293
262
|
{
|
|
294
263
|
"id": "mutation.upsert",
|
|
295
|
-
"feature": "F312",
|
|
296
264
|
"status": "supported",
|
|
297
265
|
"example": "INSERT INTO keyed (name, score) VALUES ('x', 9) ON CONFLICT (name) DO UPDATE SET score = EXCLUDED.score",
|
|
298
|
-
"notes": "
|
|
266
|
+
"notes": "PostgreSQL-compatible ON CONFLICT form. The conflict target is the unique key and assigned values may read the target row or EXCLUDED proposal."
|
|
299
267
|
},
|
|
300
268
|
{
|
|
301
269
|
"id": "mutation.insert-do-nothing",
|
|
302
|
-
"feature": "F312",
|
|
303
270
|
"status": "supported",
|
|
304
271
|
"example": "INSERT INTO keyed (name, score) VALUES ('x', 9), ('z', 1) ON CONFLICT (name) DO NOTHING",
|
|
305
|
-
"notes": "Rows whose key already exists at the statement's snapshot are skipped."
|
|
272
|
+
"notes": "Rows whose key already exists at the statement's snapshot are skipped. Each proposed row is considered in order, so later duplicates of a key retained earlier in the same statement are skipped too."
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
"id": "mutation.upsert-replace",
|
|
276
|
+
"status": "supported",
|
|
277
|
+
"example": "INSERT INTO keyed (name, score, bonus) VALUES ('x', 50, 9) ON CONFLICT (name) DO REPLACE",
|
|
278
|
+
"notes": "Minnow's concise whole-row upsert spelling replaces every non-key column and also has exact semantics for a key-only table."
|
|
306
279
|
},
|
|
307
280
|
{
|
|
308
281
|
"id": "mutation.upsert-partial",
|
|
309
|
-
"feature": "F312",
|
|
310
282
|
"status": "supported",
|
|
311
283
|
"example": "INSERT INTO keyed (name, score, bonus) VALUES ('x', 50, 9) ON CONFLICT (name) DO UPDATE SET score = EXCLUDED.score",
|
|
312
|
-
"notes": "Assigning a subset of
|
|
284
|
+
"notes": "Assigning a subset of target columns changes only those columns; unassigned columns keep their stored values. Assignment targets need not appear in the INSERT column list. Mixed update/insert batches publish atomically or roll back together."
|
|
313
285
|
},
|
|
314
286
|
{
|
|
315
287
|
"id": "where.or",
|
|
316
|
-
"feature": "E061-14",
|
|
317
288
|
"status": "supported",
|
|
318
289
|
"example": "SELECT region FROM rows WHERE amount > 5 OR region = 'west'"
|
|
319
290
|
},
|
|
320
291
|
{
|
|
321
292
|
"id": "where.like",
|
|
322
|
-
"feature": "E061-04",
|
|
323
293
|
"status": "supported",
|
|
324
294
|
"example": "SELECT region FROM rows WHERE region LIKE 'w%'",
|
|
325
295
|
"notes": "% matches any run and _ matches one Unicode codepoint."
|
|
326
296
|
},
|
|
327
297
|
{
|
|
328
298
|
"id": "predicate.is-distinct-from",
|
|
329
|
-
"feature": "T151",
|
|
330
299
|
"status": "supported",
|
|
331
300
|
"example": "SELECT region FROM rows WHERE region IS DISTINCT FROM 'west'",
|
|
332
301
|
"notes": "Null-safe: NULL is not distinct from NULL."
|
|
333
302
|
},
|
|
334
303
|
{
|
|
335
304
|
"id": "predicate.boolean-test",
|
|
336
|
-
"feature": "T031",
|
|
337
305
|
"status": "supported",
|
|
338
306
|
"example": "SELECT region FROM rows WHERE active IS TRUE OR active IS UNKNOWN",
|
|
339
307
|
"notes": "IS [NOT] TRUE/FALSE/UNKNOWN never return UNKNOWN; they desugar to null-safe comparisons."
|
|
340
308
|
},
|
|
341
309
|
{
|
|
342
310
|
"id": "predicate.like-escape",
|
|
343
|
-
"feature": "E061-05",
|
|
344
311
|
"status": "supported",
|
|
345
312
|
"example": "SELECT region FROM rows WHERE region LIKE 'we!%st' ESCAPE '!' OR region LIKE 'we%'",
|
|
346
313
|
"notes": "ESCAPE makes the next pattern character literal, wildcards included."
|
|
347
314
|
},
|
|
348
315
|
{
|
|
349
316
|
"id": "predicate.quantified",
|
|
350
|
-
"feature": "E061-07",
|
|
351
317
|
"status": "supported",
|
|
352
318
|
"example": "SELECT region FROM rows WHERE amount > ALL (SELECT amount FROM dims)",
|
|
353
319
|
"notes": "ANY/SOME/ALL with full three-valued logic; correlated forms are rejected. SQLite itself has no quantified comparisons."
|
|
354
320
|
},
|
|
355
321
|
{
|
|
356
322
|
"id": "predicate.ilike",
|
|
357
|
-
"feature": "minnow",
|
|
358
323
|
"status": "supported",
|
|
359
324
|
"example": "SELECT region FROM rows WHERE region ILIKE 'WE%'",
|
|
360
325
|
"notes": "Case-insensitive LIKE, a PostgreSQL extension; SQLite's LIKE is case-insensitive by default instead."
|
|
361
326
|
},
|
|
362
327
|
{
|
|
363
328
|
"id": "predicate.match",
|
|
364
|
-
"feature": "minnow",
|
|
365
329
|
"status": "supported",
|
|
366
330
|
"example": "SELECT region FROM rows WHERE MATCH(region) AGAINST 'west'"
|
|
367
331
|
},
|
|
368
332
|
{
|
|
369
333
|
"id": "predicate.match-star",
|
|
370
|
-
"feature": "minnow",
|
|
371
334
|
"status": "supported",
|
|
372
335
|
"example": "SELECT region FROM rows WHERE MATCH(*) AGAINST 'wes*'"
|
|
373
336
|
},
|
|
374
337
|
{
|
|
375
338
|
"id": "predicate.match-parameter",
|
|
376
|
-
"feature": "minnow",
|
|
377
339
|
"status": "supported",
|
|
378
340
|
"example": "SELECT region FROM rows WHERE MATCH(region) AGAINST $1 ORDER BY BM25(region) AGAINST $1 DESC",
|
|
379
341
|
"params": ["west"],
|
|
@@ -381,236 +343,198 @@
|
|
|
381
343
|
},
|
|
382
344
|
{
|
|
383
345
|
"id": "function.bm25",
|
|
384
|
-
"feature": "minnow",
|
|
385
346
|
"status": "supported",
|
|
386
347
|
"example": "SELECT region, BM25(region) AGAINST 'west' AS score FROM rows WHERE MATCH(region) AGAINST 'west' ORDER BY score DESC"
|
|
387
348
|
},
|
|
388
349
|
{
|
|
389
350
|
"id": "order-by.expression",
|
|
390
|
-
"feature": "E121",
|
|
391
351
|
"status": "supported",
|
|
392
352
|
"example": "SELECT region FROM rows WHERE amount > 0 ORDER BY amount * 2 DESC, region"
|
|
393
353
|
},
|
|
394
354
|
{
|
|
395
355
|
"id": "where.between",
|
|
396
|
-
"feature": "E061-02",
|
|
397
356
|
"status": "supported",
|
|
398
357
|
"example": "SELECT region FROM rows WHERE amount BETWEEN 1 AND 5"
|
|
399
358
|
},
|
|
400
359
|
{
|
|
401
360
|
"id": "where.between-symmetric",
|
|
402
|
-
"feature": "T461",
|
|
403
361
|
"status": "supported",
|
|
404
362
|
"example": "SELECT region FROM rows WHERE amount BETWEEN SYMMETRIC 5 AND 1",
|
|
405
363
|
"notes": "SYMMETRIC accepts the bounds in either order."
|
|
406
364
|
},
|
|
407
365
|
{
|
|
408
366
|
"id": "where.is-null",
|
|
409
|
-
"feature": "E061-06",
|
|
410
367
|
"status": "supported",
|
|
411
368
|
"example": "SELECT region FROM rows WHERE region IS NULL"
|
|
412
369
|
},
|
|
413
370
|
{
|
|
414
371
|
"id": "where.is-not-null",
|
|
415
|
-
"feature": "E061-06",
|
|
416
372
|
"status": "supported",
|
|
417
373
|
"example": "SELECT amount FROM rows WHERE region IS NOT NULL"
|
|
418
374
|
},
|
|
419
375
|
{
|
|
420
376
|
"id": "where.exists",
|
|
421
|
-
"feature": "E061-08",
|
|
422
377
|
"status": "supported",
|
|
423
378
|
"example": "SELECT region FROM rows WHERE EXISTS (SELECT 1 FROM dims)",
|
|
424
|
-
"notes": "
|
|
379
|
+
"notes": "Both uncorrelated and correlated EXISTS are supported. Correlated forms are decorrelated into joins rather than executed once per outer row."
|
|
425
380
|
},
|
|
426
381
|
{
|
|
427
382
|
"id": "expression.case",
|
|
428
|
-
"feature": "F261-02",
|
|
429
383
|
"status": "supported",
|
|
430
384
|
"example": "SELECT CASE WHEN amount > 5 THEN 'big' ELSE 'small' END AS size FROM rows"
|
|
431
385
|
},
|
|
432
386
|
{
|
|
433
387
|
"id": "subquery.correlated",
|
|
434
|
-
"feature": "E061-13",
|
|
435
388
|
"status": "supported",
|
|
436
389
|
"example": "SELECT region FROM rows r WHERE amount > (SELECT AVG(amount) FROM rows q WHERE q.region = r.region)",
|
|
437
390
|
"notes": "Equality-correlated subqueries decorrelate into derived-table joins at compile time; both executors run plain joins."
|
|
438
391
|
},
|
|
439
392
|
{
|
|
440
393
|
"id": "subquery.correlated-exists",
|
|
441
|
-
"feature": "E061-13",
|
|
442
394
|
"status": "supported",
|
|
443
395
|
"example": "SELECT amount FROM rows r WHERE EXISTS (SELECT region FROM dims d WHERE d.region = r.region)",
|
|
444
396
|
"notes": "EXISTS joins the subquery's distinct correlation keys; NOT EXISTS becomes a left join checked with IS NULL."
|
|
445
397
|
},
|
|
446
398
|
{
|
|
447
399
|
"id": "subquery.correlated-select",
|
|
448
|
-
"feature": "E061-13",
|
|
449
400
|
"status": "supported",
|
|
450
401
|
"example": "SELECT r.region, (SELECT AVG(q.amount) FROM rows q WHERE q.region = r.region) AS regional FROM rows r",
|
|
451
402
|
"notes": "Correlated scalar aggregates decorrelate in the select list too, outside grouped queries."
|
|
452
403
|
},
|
|
453
404
|
{
|
|
454
405
|
"id": "subquery.correlated-non-equi",
|
|
455
|
-
"
|
|
456
|
-
"
|
|
457
|
-
"
|
|
458
|
-
"error": "support only equality conditions",
|
|
459
|
-
"notes": "Correlation must be a plain equality between one inner and one outer qualified column."
|
|
406
|
+
"status": "supported",
|
|
407
|
+
"example": "SELECT r.amount FROM rows r WHERE EXISTS (SELECT q.amount FROM rows q WHERE q.amount < r.amount)",
|
|
408
|
+
"notes": "Non-equality EXISTS and NOT EXISTS correlations lower to semi-joins and anti-joins; correlated scalar aggregates and IN remain equality-only."
|
|
460
409
|
},
|
|
461
410
|
{
|
|
462
411
|
"id": "subquery.correlated-not-in",
|
|
463
|
-
"
|
|
464
|
-
"
|
|
465
|
-
"example": "SELECT region FROM rows r WHERE region NOT IN (SELECT d.region FROM dims d WHERE d.region = r.region)",
|
|
466
|
-
"error": "use NOT EXISTS",
|
|
467
|
-
"notes": "Correlated NOT IN has NULL semantics that the join rewrite cannot preserve; NOT EXISTS expresses the intent."
|
|
412
|
+
"status": "supported",
|
|
413
|
+
"example": "SELECT region FROM rows r WHERE region NOT IN (SELECT d.region FROM dims d WHERE d.region = r.region)"
|
|
468
414
|
},
|
|
469
415
|
{
|
|
470
416
|
"id": "cte.recursive",
|
|
471
|
-
"feature": "T131",
|
|
472
417
|
"status": "supported",
|
|
473
418
|
"example": "WITH RECURSIVE n AS (SELECT MIN(amount) AS v FROM rows UNION ALL SELECT v + 1 FROM n WHERE v < 6) SELECT v FROM n",
|
|
474
419
|
"notes": "Linear delta recursion with UNION or UNION ALL, capped at 10,000 iterations and 1,000,000 rows. Plain WITH still rejects self-references."
|
|
475
420
|
},
|
|
476
421
|
{
|
|
477
422
|
"id": "mutation.with-cte",
|
|
478
|
-
"feature": "T121",
|
|
479
423
|
"status": "supported",
|
|
480
424
|
"example": "WITH totals AS (SELECT MAX(score) AS top FROM keyed) DELETE FROM keyed WHERE score >= (SELECT top FROM totals) RETURNING name",
|
|
481
425
|
"notes": "WITH precedes INSERT/UPDATE/DELETE; the CTEs are visible to the statement's queries and subqueries."
|
|
482
426
|
},
|
|
483
427
|
{
|
|
484
428
|
"id": "set.intersect",
|
|
485
|
-
"feature": "F302-01",
|
|
486
429
|
"status": "supported",
|
|
487
430
|
"example": "SELECT region FROM rows INTERSECT SELECT region FROM dims",
|
|
488
|
-
"notes": "INTERSECT binds tighter than UNION and EXCEPT,
|
|
431
|
+
"notes": "INTERSECT binds tighter than UNION and EXCEPT, matching PostgreSQL."
|
|
489
432
|
},
|
|
490
433
|
{
|
|
491
434
|
"id": "set.except",
|
|
492
|
-
"feature": "E071-03",
|
|
493
435
|
"status": "supported",
|
|
494
436
|
"example": "SELECT region FROM rows EXCEPT SELECT region FROM dims"
|
|
495
437
|
},
|
|
496
438
|
{
|
|
497
439
|
"id": "set.intersect-all",
|
|
498
|
-
"feature": "F302-02",
|
|
499
440
|
"status": "supported",
|
|
500
441
|
"example": "SELECT region FROM rows INTERSECT ALL SELECT region FROM dims",
|
|
501
442
|
"notes": "Bag semantics; SQLite itself has no INTERSECT ALL."
|
|
502
443
|
},
|
|
503
444
|
{
|
|
504
445
|
"id": "set.except-all",
|
|
505
|
-
"feature": "F304",
|
|
506
446
|
"status": "supported",
|
|
507
447
|
"example": "SELECT region FROM rows EXCEPT ALL SELECT region FROM dims",
|
|
508
448
|
"notes": "Bag semantics; SQLite itself has no EXCEPT ALL."
|
|
509
449
|
},
|
|
510
450
|
{
|
|
511
451
|
"id": "aggregate.count-distinct",
|
|
512
|
-
"feature": "E091-07",
|
|
513
452
|
"status": "supported",
|
|
514
453
|
"example": "SELECT COUNT(DISTINCT region) AS regions FROM rows"
|
|
515
454
|
},
|
|
516
455
|
{
|
|
517
456
|
"id": "aggregate.filter",
|
|
518
|
-
"feature": "T612",
|
|
519
457
|
"status": "supported",
|
|
520
458
|
"example": "SELECT region, COUNT(*) FILTER (WHERE amount > 5) AS big FROM rows GROUP BY region",
|
|
521
459
|
"notes": "Desugars into a CASE inside the aggregate, so it works with every aggregate and DISTINCT."
|
|
522
460
|
},
|
|
523
461
|
{
|
|
524
462
|
"id": "window.aggregate-over",
|
|
525
|
-
"feature": "T611",
|
|
526
463
|
"status": "supported",
|
|
527
464
|
"example": "SELECT SUM(amount) OVER (PARTITION BY region) AS total FROM rows",
|
|
528
465
|
"notes": "Default frame only: whole partition without OVER ordering, running with peers when ordered."
|
|
529
466
|
},
|
|
530
467
|
{
|
|
531
468
|
"id": "window.in-expression",
|
|
532
|
-
"feature": "T611",
|
|
533
469
|
"status": "supported",
|
|
534
470
|
"example": "SELECT amount, amount - LAG(amount) OVER (ORDER BY amount, region) AS change, 100.0 * amount / SUM(amount) OVER () AS pct FROM rows",
|
|
535
471
|
"notes": "A window is an expression: the arithmetic around it is evaluated after the window has run, over the column it produced."
|
|
536
472
|
},
|
|
537
473
|
{
|
|
538
474
|
"id": "window.over-grouped",
|
|
539
|
-
"feature": "T611",
|
|
540
475
|
"status": "supported",
|
|
541
476
|
"example": "SELECT region, SUM(amount) AS total, ROW_NUMBER() OVER (ORDER BY SUM(amount) DESC, region) AS rank, SUM(SUM(amount)) OVER () AS everything FROM rows GROUP BY region HAVING COUNT(*) > 0",
|
|
542
|
-
"notes": "Windows run after GROUP BY and HAVING,
|
|
477
|
+
"notes": "Windows run after GROUP BY and HAVING, matching PostgreSQL, so they rank groups and their OVER clause reads the group's aggregates."
|
|
543
478
|
},
|
|
544
479
|
{
|
|
545
480
|
"id": "window.value-functions",
|
|
546
|
-
"feature": "T617",
|
|
547
481
|
"status": "supported",
|
|
548
482
|
"example": "SELECT amount, FIRST_VALUE(amount) OVER (PARTITION BY region ORDER BY amount) AS lowest, LAST_VALUE(amount) OVER (PARTITION BY region ORDER BY amount ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS highest FROM rows",
|
|
549
|
-
"notes": "FIRST_VALUE/LAST_VALUE respect the frame; the default frame ends at the current peer group
|
|
483
|
+
"notes": "FIRST_VALUE/LAST_VALUE respect the frame; matching PostgreSQL, the default frame ends at the current peer group."
|
|
550
484
|
},
|
|
551
485
|
{
|
|
552
486
|
"id": "window.ntile",
|
|
553
|
-
"feature": "T614",
|
|
554
487
|
"status": "supported",
|
|
555
488
|
"example": "SELECT amount, NTILE(2) OVER (ORDER BY amount) AS half FROM rows"
|
|
556
489
|
},
|
|
557
490
|
{
|
|
558
491
|
"id": "window.distribution",
|
|
559
|
-
"feature": "T612",
|
|
560
492
|
"status": "supported",
|
|
561
493
|
"example": "SELECT amount, PERCENT_RANK() OVER (ORDER BY amount) AS pr, CUME_DIST() OVER (ORDER BY amount) AS cd FROM rows"
|
|
562
494
|
},
|
|
563
495
|
{
|
|
564
496
|
"id": "window.frame",
|
|
565
|
-
"feature": "T612",
|
|
566
497
|
"status": "supported",
|
|
567
498
|
"example": "SELECT amount, SUM(amount) OVER (ORDER BY amount, joined ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS windowed FROM rows",
|
|
568
499
|
"notes": "ROWS frames take row-distance bounds; RANGE frames take UNBOUNDED and CURRENT ROW bounds, where CURRENT ROW spans the ordering peer group."
|
|
569
500
|
},
|
|
570
501
|
{
|
|
571
502
|
"id": "join.right",
|
|
572
|
-
"feature": "F041-04",
|
|
573
503
|
"status": "supported",
|
|
574
504
|
"example": "SELECT r.region FROM rows r RIGHT JOIN dims d ON d.region = r.region",
|
|
575
505
|
"notes": "Desugars to the mirrored LEFT JOIN; supported as the sole join of a block."
|
|
576
506
|
},
|
|
577
507
|
{
|
|
578
508
|
"id": "join.non-equi",
|
|
579
|
-
"feature": "F041-08",
|
|
580
509
|
"status": "supported",
|
|
581
510
|
"example": "SELECT r.region FROM rows r JOIN dims d ON d.amount > r.amount",
|
|
582
511
|
"notes": "Executes as a nested-loop join (probe x build); equalities keep the hash path."
|
|
583
512
|
},
|
|
584
513
|
{
|
|
585
514
|
"id": "select.distinct-wildcard",
|
|
586
|
-
"feature": "E051-01",
|
|
587
515
|
"status": "supported",
|
|
588
516
|
"example": "SELECT DISTINCT * FROM rows",
|
|
589
517
|
"notes": "Expands to DISTINCT over every wildcard output column once input schemas are known."
|
|
590
518
|
},
|
|
591
519
|
{
|
|
592
520
|
"id": "limit.offset",
|
|
593
|
-
"feature": "F856",
|
|
594
521
|
"status": "supported",
|
|
595
522
|
"example": "SELECT amount FROM rows LIMIT 5 OFFSET 2",
|
|
596
523
|
"notes": "OFFSET is accepted directly after LIMIT."
|
|
597
524
|
},
|
|
598
525
|
{
|
|
599
526
|
"id": "select.no-from",
|
|
600
|
-
"feature": "E051",
|
|
601
527
|
"status": "supported",
|
|
602
528
|
"example": "SELECT 1 + 1 AS two, UPPER('minnow') AS name"
|
|
603
529
|
},
|
|
604
530
|
{
|
|
605
531
|
"id": "select.values",
|
|
606
|
-
"feature": "F641",
|
|
607
532
|
"status": "supported",
|
|
608
533
|
"example": "SELECT v.column1 AS n, v.column2 AS tag FROM (VALUES (1, 'one'), (2, 'two')) v",
|
|
609
534
|
"notes": "VALUES works standalone, as a set-operation member, and as a derived table with AS alias(col, ...) renaming; columns default to column1..columnN."
|
|
610
535
|
},
|
|
611
536
|
{
|
|
612
537
|
"id": "limit.parameter",
|
|
613
|
-
"feature": "F865",
|
|
614
538
|
"status": "supported",
|
|
615
539
|
"example": "SELECT region, amount FROM rows ORDER BY region NULLS LAST, amount LIMIT $1 OFFSET $2",
|
|
616
540
|
"params": [2, 1],
|
|
@@ -618,28 +542,24 @@
|
|
|
618
542
|
},
|
|
619
543
|
{
|
|
620
544
|
"id": "limit.fetch-first",
|
|
621
|
-
"feature": "F856",
|
|
622
545
|
"status": "supported",
|
|
623
546
|
"example": "SELECT amount FROM rows ORDER BY amount OFFSET 1 ROWS FETCH FIRST 2 ROWS ONLY",
|
|
624
|
-
"notes": "
|
|
547
|
+
"notes": "PostgreSQL's FETCH clause is accepted as a spelling of LIMIT; SQLite itself only speaks LIMIT."
|
|
625
548
|
},
|
|
626
549
|
{
|
|
627
550
|
"id": "offset.standalone",
|
|
628
|
-
"feature": "F856",
|
|
629
551
|
"status": "supported",
|
|
630
552
|
"example": "SELECT amount FROM rows ORDER BY amount OFFSET 2",
|
|
631
553
|
"notes": "OFFSET no longer requires LIMIT. SQLite itself needs LIMIT -1 OFFSET n."
|
|
632
554
|
},
|
|
633
555
|
{
|
|
634
556
|
"id": "ddl.create-table",
|
|
635
|
-
"feature": "F031-01",
|
|
636
557
|
"status": "supported",
|
|
637
558
|
"example": "CREATE TABLE made (id INTEGER PRIMARY KEY, label TEXT NOT NULL, at TIMESTAMP)",
|
|
638
|
-
"notes": "
|
|
559
|
+
"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."
|
|
639
560
|
},
|
|
640
561
|
{
|
|
641
562
|
"id": "mutation.update-keyless",
|
|
642
|
-
"feature": "E101-03",
|
|
643
563
|
"status": "unsupported",
|
|
644
564
|
"example": "UPDATE rows SET amount = 1",
|
|
645
565
|
"error": "UPDATE requires a table with a unique key",
|
|
@@ -647,21 +567,18 @@
|
|
|
647
567
|
},
|
|
648
568
|
{
|
|
649
569
|
"id": "trigger.create-after",
|
|
650
|
-
"feature": "T211",
|
|
651
570
|
"status": "supported",
|
|
652
571
|
"example": "CREATE TRIGGER keyed_audit AFTER INSERT ON keyed BEGIN INSERT INTO rows (region, amount) VALUES (NEW.name, NEW.score); END",
|
|
653
572
|
"notes": "AFTER and BEFORE row triggers on INSERT/UPDATE/DELETE, executed atomically inside the triggering commit with NEW/OLD references. Bodies: INSERT ... VALUES into keyless tables; UPDATE/DELETE against keyed tables. One cascade level is allowed; deeper chains error at write time."
|
|
654
573
|
},
|
|
655
574
|
{
|
|
656
575
|
"id": "trigger.create-before",
|
|
657
|
-
"feature": "T211",
|
|
658
576
|
"status": "supported",
|
|
659
577
|
"example": "CREATE TRIGGER keyed_before BEFORE INSERT ON keyed BEGIN INSERT INTO rows (region, amount) VALUES (NEW.name, NEW.score); END",
|
|
660
578
|
"notes": "BEFORE bodies stage ahead of the primary write but publish in the same atomic commit, so timing is a portability feature: atomicity and visibility are identical to AFTER."
|
|
661
579
|
},
|
|
662
580
|
{
|
|
663
581
|
"id": "trigger.body-update-delete",
|
|
664
|
-
"feature": "T211",
|
|
665
582
|
"status": "supported",
|
|
666
583
|
"example": "CREATE TRIGGER keyed_counts AFTER INSERT ON keyed BEGIN UPDATE stats SET total = total + NEW.score WHERE region = NEW.name; END",
|
|
667
584
|
"setup": [
|
|
@@ -672,7 +589,6 @@
|
|
|
672
589
|
},
|
|
673
590
|
{
|
|
674
591
|
"id": "trigger.drop",
|
|
675
|
-
"feature": "T211",
|
|
676
592
|
"status": "supported",
|
|
677
593
|
"example": "DROP TRIGGER droppable_audit",
|
|
678
594
|
"setup": [
|
|
@@ -681,185 +597,158 @@
|
|
|
681
597
|
},
|
|
682
598
|
{
|
|
683
599
|
"id": "where.parenthesized",
|
|
684
|
-
"feature": "E061-14",
|
|
685
600
|
"status": "supported",
|
|
686
601
|
"example": "SELECT region FROM rows WHERE (amount > 5 AND region = 'west')"
|
|
687
602
|
},
|
|
688
603
|
{
|
|
689
604
|
"id": "where.not",
|
|
690
|
-
"feature": "E061-14",
|
|
691
605
|
"status": "supported",
|
|
692
606
|
"example": "SELECT region FROM rows WHERE NOT active = TRUE"
|
|
693
607
|
},
|
|
694
608
|
{
|
|
695
609
|
"id": "expression.concat",
|
|
696
|
-
"feature": "E021-07",
|
|
697
610
|
"status": "supported",
|
|
698
611
|
"example": "SELECT region || '-' || label AS tag FROM dims",
|
|
699
612
|
"notes": "|| concatenates strings and propagates NULL; non-string operands are a type error."
|
|
700
613
|
},
|
|
701
614
|
{
|
|
702
615
|
"id": "expression.modulo",
|
|
703
|
-
"feature": "T441",
|
|
704
616
|
"status": "supported",
|
|
705
617
|
"example": "SELECT amount % 3 AS remainder FROM rows",
|
|
706
618
|
"notes": "Division and remainder by zero are NULL, matching SQLite."
|
|
707
619
|
},
|
|
708
620
|
{
|
|
709
621
|
"id": "expression.cast",
|
|
710
|
-
"feature": "F201",
|
|
711
622
|
"status": "supported",
|
|
712
623
|
"example": "SELECT CAST(amount AS INTEGER) AS whole, CAST(amount AS TEXT) AS label FROM rows",
|
|
713
|
-
"notes": "
|
|
624
|
+
"notes": "Common PostgreSQL type names map to the four stored value kinds; integer targets truncate toward zero, unlike PostgreSQL's rounding, and non-numeric strings fail rather than becoming 0."
|
|
714
625
|
},
|
|
715
626
|
{
|
|
716
627
|
"id": "identifier.quoted",
|
|
717
|
-
"feature": "E031-01",
|
|
718
628
|
"status": "supported",
|
|
719
629
|
"example": "SELECT \"region\", \"rows\".\"amount\" FROM \"rows\" WHERE \"amount\" > 5",
|
|
720
630
|
"notes": "Double-quoted identifiers are never keywords and keep their exact spelling."
|
|
721
631
|
},
|
|
722
632
|
{
|
|
723
633
|
"id": "order-by.nulls",
|
|
724
|
-
"feature": "T611",
|
|
725
634
|
"status": "supported",
|
|
726
635
|
"example": "SELECT region, amount FROM rows ORDER BY region NULLS LAST, amount",
|
|
727
|
-
"notes": "Without NULLS FIRST/LAST the default
|
|
636
|
+
"notes": "Without NULLS FIRST/LAST the default follows PostgreSQL: NULLs last ascending, first descending."
|
|
728
637
|
},
|
|
729
638
|
{
|
|
730
639
|
"id": "expression.coalesce",
|
|
731
|
-
"feature": "F261-04",
|
|
732
640
|
"status": "supported",
|
|
733
641
|
"example": "SELECT COALESCE(region, 'unknown') AS region_label FROM rows",
|
|
734
642
|
"notes": "Arguments evaluate left to right; the first non-NULL value wins. All non-NULL arguments must share one type."
|
|
735
643
|
},
|
|
736
644
|
{
|
|
737
645
|
"id": "expression.date-trunc",
|
|
738
|
-
"feature": "minnow",
|
|
739
646
|
"status": "supported",
|
|
740
647
|
"example": "SELECT DATE_TRUNC('month', joined) AS joined_month FROM rows",
|
|
741
648
|
"notes": "Units: year, quarter, month, week (Monday start), day, hour, minute, second. Truncation is in UTC; the engine has no session time zone."
|
|
742
649
|
},
|
|
743
650
|
{
|
|
744
651
|
"id": "expression.date-add",
|
|
745
|
-
"feature": "F052",
|
|
746
652
|
"status": "supported",
|
|
747
653
|
"example": "SELECT joined + INTERVAL '1 month' AS next_month, joined - INTERVAL '2 days 3 hours' AS earlier FROM rows WHERE joined IS NOT NULL",
|
|
748
654
|
"notes": "INTERVAL added to or subtracted from a datetime. Months are calendar arithmetic, so 31 January plus a month clamps to the end of February."
|
|
749
655
|
},
|
|
750
656
|
{
|
|
751
657
|
"id": "function.string-core",
|
|
752
|
-
"feature": "E021-08",
|
|
753
658
|
"status": "supported",
|
|
754
659
|
"example": "SELECT UPPER(label) AS u, LOWER(label) AS l, LENGTH(label) AS n, SUBSTR(label, 2, 3) AS mid, TRIM(label) AS t FROM dims",
|
|
755
660
|
"notes": "SUBSTRING is accepted as a spelling of SUBSTR; LENGTH and SUBSTR count characters, not UTF-16 units."
|
|
756
661
|
},
|
|
757
662
|
{
|
|
758
663
|
"id": "function.abs",
|
|
759
|
-
"feature": "T441",
|
|
760
664
|
"status": "supported",
|
|
761
665
|
"example": "SELECT ABS(amount - 5) AS distance FROM rows"
|
|
762
666
|
},
|
|
763
667
|
{
|
|
764
668
|
"id": "function.numeric-core",
|
|
765
|
-
"feature": "T441",
|
|
766
669
|
"status": "supported",
|
|
767
670
|
"example": "SELECT NULLIF(amount, 3) AS n, GREATEST(amount, 5) AS g, LEAST(amount, 5) AS l, FLOOR(amount) AS f, CEILING(amount) AS c, MOD(amount, 4) AS m, POWER(2, 3) AS p, SQRT(16) AS s FROM rows",
|
|
768
671
|
"notes": "GREATEST/LEAST ignore NULL arguments, matching PostgreSQL."
|
|
769
672
|
},
|
|
770
673
|
{
|
|
771
674
|
"id": "function.string-extended",
|
|
772
|
-
"feature": "E021-06",
|
|
773
675
|
"status": "supported",
|
|
774
676
|
"example": "SELECT REPLACE(region, 'we', 'be') AS r, LTRIM(' x') AS lt, RTRIM('x ') AS rt, INSTR(region, 'st') AS i FROM rows WHERE region IS NOT NULL"
|
|
775
677
|
},
|
|
776
678
|
{
|
|
777
679
|
"id": "function.extract",
|
|
778
|
-
"feature": "F052",
|
|
779
680
|
"status": "supported",
|
|
780
681
|
"example": "SELECT EXTRACT(year FROM joined) AS y, EXTRACT(dow FROM joined) AS d FROM rows WHERE joined IS NOT NULL",
|
|
781
682
|
"notes": "Fields: year, quarter, month, week (ISO), day, hour, minute, second, epoch, dow — all in UTC. SQLite spells this strftime."
|
|
782
683
|
},
|
|
783
684
|
{
|
|
784
685
|
"id": "aggregate.distinct-argument",
|
|
785
|
-
"feature": "E091-07",
|
|
786
686
|
"status": "supported",
|
|
787
687
|
"example": "SELECT region, COUNT(DISTINCT amount) AS amounts, COUNT(DISTINCT active) AS states, SUM(amount) AS total FROM rows GROUP BY region",
|
|
788
688
|
"notes": "COUNT/SUM/AVG/MIN/MAX accept DISTINCT. Each one keeps its own set of values, so several can appear in one select, beside ordinary aggregates, inside expressions, and in HAVING."
|
|
789
689
|
},
|
|
790
690
|
{
|
|
791
691
|
"id": "join.multi-key",
|
|
792
|
-
"feature": "F041-01",
|
|
793
692
|
"status": "supported",
|
|
794
693
|
"example": "SELECT r.region FROM rows r JOIN dims d ON d.region = r.region AND d.amount = r.amount",
|
|
795
694
|
"notes": "Multi-key conditions execute as a nested-loop join; single equalities keep the hash path."
|
|
796
695
|
},
|
|
797
696
|
{
|
|
798
697
|
"id": "join.cross",
|
|
799
|
-
"feature": "F401-04",
|
|
800
698
|
"status": "supported",
|
|
801
699
|
"example": "SELECT r.region AS region, d.label AS label FROM rows r CROSS JOIN dims d"
|
|
802
700
|
},
|
|
803
701
|
{
|
|
804
702
|
"id": "join.full",
|
|
805
|
-
"feature": "F401-02",
|
|
806
703
|
"status": "supported",
|
|
807
704
|
"example": "SELECT r.amount AS amount, d.label AS label FROM rows r FULL JOIN dims d ON d.region = r.region",
|
|
808
705
|
"notes": "Desugars into a union of two left joins, so it must be the sole join, with an equality ON and no grouping or DISTINCT yet."
|
|
809
706
|
},
|
|
810
707
|
{
|
|
811
708
|
"id": "order-by.ordinal",
|
|
812
|
-
"feature": "E121",
|
|
813
709
|
"status": "supported",
|
|
814
710
|
"example": "SELECT region, amount FROM rows ORDER BY 2 DESC",
|
|
815
711
|
"notes": "Ordinals resolve to the select list at compile time; out-of-range ordinals are an error."
|
|
816
712
|
},
|
|
817
713
|
{
|
|
818
714
|
"id": "window.lag-lead",
|
|
819
|
-
"feature": "T615",
|
|
820
715
|
"status": "supported",
|
|
821
716
|
"example": "SELECT amount, LAG(amount) OVER (ORDER BY amount) AS previous, LEAD(amount, 1, -1) OVER (ORDER BY amount) AS next FROM rows",
|
|
822
717
|
"notes": "LAG/LEAD take a constant offset (default 1) and default value (default NULL), and require ORDER BY inside OVER."
|
|
823
718
|
},
|
|
824
719
|
{
|
|
825
720
|
"id": "mutation.insert-select",
|
|
826
|
-
"feature": "E101-01",
|
|
827
721
|
"status": "supported",
|
|
828
722
|
"example": "INSERT INTO keyed (name, score) SELECT name || '2' AS name, score + 1 AS score FROM keyed",
|
|
829
723
|
"notes": "The SELECT runs at one snapshot and materializes before the batch write."
|
|
830
724
|
},
|
|
831
725
|
{
|
|
832
726
|
"id": "mutation.merge",
|
|
833
|
-
"feature": "F312",
|
|
834
727
|
"status": "supported",
|
|
835
728
|
"setup": ["INSERT INTO keyed (name, score, bonus) VALUES ('z', 5, NULL)"],
|
|
836
729
|
"example": "MERGE INTO keyed k USING (SELECT 'z' AS name, 9 AS score) s ON k.name = s.name WHEN MATCHED THEN UPDATE SET score = s.score WHEN NOT MATCHED THEN INSERT (name, score) VALUES (s.name, s.score)",
|
|
837
|
-
"notes": "One pass over the source decides each row's branch, and the branches apply as batched writes inside a single write scope — atomic, and firing the same triggers the equivalent INSERT, UPDATE, and DELETE would. The match must equate the target's unique key with a source value, which is how rows are addressed.
|
|
730
|
+
"notes": "One pass over the source decides each row's branch, and the branches apply as batched writes inside a single write scope — atomic, and firing the same triggers the equivalent INSERT, UPDATE, and DELETE would. The match must equate the target's unique key with a source value, which is how rows are addressed. Matching PostgreSQL, two source rows cannot act on one target row. MATCHED BY SOURCE, MATCHED BY TARGET, and RETURNING are not supported."
|
|
838
731
|
},
|
|
839
732
|
{
|
|
840
733
|
"id": "transaction.begin",
|
|
841
|
-
"feature": "E151-01",
|
|
842
734
|
"status": "supported",
|
|
843
735
|
"example": "BEGIN",
|
|
844
736
|
"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."
|
|
845
737
|
},
|
|
846
738
|
{
|
|
847
739
|
"id": "transaction.commit",
|
|
848
|
-
"feature": "E151-01",
|
|
849
740
|
"status": "supported",
|
|
850
741
|
"setup": ["BEGIN"],
|
|
851
742
|
"example": "COMMIT"
|
|
852
743
|
},
|
|
853
744
|
{
|
|
854
745
|
"id": "transaction.rollback",
|
|
855
|
-
"feature": "E151-02",
|
|
856
746
|
"status": "supported",
|
|
857
747
|
"setup": ["BEGIN"],
|
|
858
748
|
"example": "ROLLBACK"
|
|
859
749
|
},
|
|
860
750
|
{
|
|
861
751
|
"id": "transaction.isolation-level",
|
|
862
|
-
"feature": "E152-01",
|
|
863
752
|
"status": "unsupported",
|
|
864
753
|
"example": "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE",
|
|
865
754
|
"error": "Expected SELECT, found SET",
|
|
@@ -867,304 +756,358 @@
|
|
|
867
756
|
},
|
|
868
757
|
{
|
|
869
758
|
"id": "function.char-length",
|
|
870
|
-
"feature": "E021-04",
|
|
871
759
|
"status": "supported",
|
|
872
760
|
"example": "SELECT CHAR_LENGTH(region) AS n FROM rows WHERE region IS NOT NULL"
|
|
873
761
|
},
|
|
874
762
|
{
|
|
875
763
|
"id": "function.octet-length",
|
|
876
|
-
"feature": "E021-05",
|
|
877
764
|
"status": "supported",
|
|
878
765
|
"example": "SELECT OCTET_LENGTH(region) AS n FROM rows WHERE region IS NOT NULL",
|
|
879
766
|
"notes": "Counts the UTF-8 encoding's bytes."
|
|
880
767
|
},
|
|
881
768
|
{
|
|
882
769
|
"id": "function.substring-from-for",
|
|
883
|
-
"feature": "E021-06",
|
|
884
770
|
"status": "supported",
|
|
885
771
|
"example": "SELECT SUBSTRING(region FROM 1 FOR 2) AS part FROM rows WHERE region IS NOT NULL",
|
|
886
772
|
"notes": "The position window is intersected with the string, so a start below 1 shortens the result instead of shifting it."
|
|
887
773
|
},
|
|
888
774
|
{
|
|
889
775
|
"id": "function.trim-specification",
|
|
890
|
-
"feature": "E021-09",
|
|
891
776
|
"status": "supported",
|
|
892
777
|
"example": "SELECT TRIM(LEADING 'w' FROM region) AS trimmed FROM rows WHERE region IS NOT NULL"
|
|
893
778
|
},
|
|
894
779
|
{
|
|
895
780
|
"id": "function.trim-multi-character",
|
|
896
|
-
"feature": "T056",
|
|
897
781
|
"status": "supported",
|
|
898
782
|
"example": "SELECT TRIM(BOTH 'we' FROM region) AS trimmed FROM rows WHERE region IS NOT NULL",
|
|
899
|
-
"notes": "
|
|
783
|
+
"notes": "Minnow removes a multi-character trim string as a repeated unit; PostgreSQL reads the argument as a set of characters instead."
|
|
900
784
|
},
|
|
901
785
|
{
|
|
902
786
|
"id": "function.position",
|
|
903
|
-
"feature": "E021-11",
|
|
904
787
|
"status": "supported",
|
|
905
788
|
"example": "SELECT POSITION('es' IN region) AS at FROM rows WHERE region IS NOT NULL"
|
|
906
789
|
},
|
|
907
790
|
{
|
|
908
791
|
"id": "function.pad",
|
|
909
|
-
"feature": "T055",
|
|
910
792
|
"status": "supported",
|
|
911
793
|
"example": "SELECT LPAD(region, 6, '-') AS padded FROM rows WHERE region IS NOT NULL"
|
|
912
794
|
},
|
|
913
795
|
{
|
|
914
796
|
"id": "function.overlay",
|
|
915
|
-
"feature": "T042",
|
|
916
797
|
"status": "supported",
|
|
917
798
|
"example": "SELECT OVERLAY(region PLACING 'X' FROM 1 FOR 1) AS masked FROM rows WHERE region IS NOT NULL"
|
|
918
799
|
},
|
|
919
800
|
{
|
|
920
801
|
"id": "select.qualified-wildcard",
|
|
921
|
-
"feature": "E051-07",
|
|
922
802
|
"status": "supported",
|
|
923
803
|
"example": "SELECT rows.* FROM rows",
|
|
924
804
|
"notes": "Output names follow the rule a bare * uses: the column's own name from one source, alias-qualified from several."
|
|
925
805
|
},
|
|
926
806
|
{
|
|
927
807
|
"id": "from.column-alias-list",
|
|
928
|
-
"feature": "E051-09",
|
|
929
808
|
"status": "supported",
|
|
930
809
|
"example": "SELECT y.a AS a FROM rows AS y(a, b, c, d)"
|
|
931
810
|
},
|
|
932
811
|
{
|
|
933
812
|
"id": "aggregate.all-quantifier",
|
|
934
|
-
"feature": "E091-06",
|
|
935
813
|
"status": "supported",
|
|
936
814
|
"example": "SELECT SUM(ALL amount) AS total FROM rows"
|
|
937
815
|
},
|
|
938
816
|
{
|
|
939
817
|
"id": "derived-table.set-operation",
|
|
940
|
-
"feature": "E071-06",
|
|
941
818
|
"status": "supported",
|
|
942
819
|
"example": "SELECT s.amount AS amount FROM (SELECT amount FROM rows UNION SELECT amount FROM dims) s"
|
|
943
820
|
},
|
|
944
821
|
{
|
|
945
822
|
"id": "comment.simple",
|
|
946
|
-
"feature": "E161",
|
|
947
823
|
"status": "supported",
|
|
948
824
|
"example": "SELECT amount FROM rows -- a comment"
|
|
949
825
|
},
|
|
950
826
|
{
|
|
951
827
|
"id": "comment.bracketed",
|
|
952
|
-
"feature": "T351",
|
|
953
828
|
"status": "supported",
|
|
954
829
|
"example": "SELECT /* a comment */ amount FROM rows"
|
|
955
830
|
},
|
|
956
831
|
{
|
|
957
832
|
"id": "join.comma",
|
|
958
|
-
"feature": "F041-07",
|
|
959
833
|
"status": "supported",
|
|
960
834
|
"example": "SELECT rows.amount AS amount FROM rows, dims WHERE dims.region = rows.region"
|
|
961
835
|
},
|
|
962
836
|
{
|
|
963
837
|
"id": "join.using",
|
|
964
|
-
"feature": "F401-04",
|
|
965
838
|
"status": "supported",
|
|
966
839
|
"example": "SELECT rows.amount AS amount FROM rows JOIN dims USING (region)",
|
|
967
|
-
"notes": "
|
|
840
|
+
"notes": "Unlike PostgreSQL, joined columns stay one per side: `SELECT *` returns both and an unqualified reference to a join column is ambiguous. Qualify it, or name the side you want."
|
|
968
841
|
},
|
|
969
842
|
{
|
|
970
843
|
"id": "join.natural",
|
|
971
|
-
"feature": "F401-01",
|
|
972
844
|
"status": "supported",
|
|
973
845
|
"example": "SELECT rows.amount AS amount FROM rows NATURAL JOIN dims",
|
|
974
846
|
"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."
|
|
975
847
|
},
|
|
976
848
|
{
|
|
977
849
|
"id": "datetime.current-date",
|
|
978
|
-
"feature": "F051-06",
|
|
979
850
|
"status": "supported",
|
|
980
851
|
"example": "SELECT CURRENT_DATE > DATE '2000-01-01' AS elapsed",
|
|
981
852
|
"notes": "Resolved once per execution, so every row of a statement sees one instant; results that read the clock never memoize."
|
|
982
853
|
},
|
|
983
854
|
{
|
|
984
855
|
"id": "datetime.current-timestamp",
|
|
985
|
-
"feature": "F051-08",
|
|
986
856
|
"status": "supported",
|
|
987
857
|
"example": "SELECT CURRENT_TIMESTAMP > TIMESTAMP '2000-01-01 00:00:00' AS elapsed"
|
|
988
858
|
},
|
|
989
859
|
{
|
|
990
860
|
"id": "datetime.localtime",
|
|
991
|
-
"feature": "F051-07",
|
|
992
861
|
"status": "supported",
|
|
993
862
|
"example": "SELECT LOCALTIME IS NOT NULL AS ticking",
|
|
994
863
|
"notes": "The engine has no TIME type, so LOCALTIME reads as an 'HH:MM:SS' string, like SQLite's CURRENT_TIME."
|
|
995
864
|
},
|
|
996
865
|
{
|
|
997
866
|
"id": "predicate.row-comparison",
|
|
998
|
-
"feature": "F641",
|
|
999
867
|
"status": "supported",
|
|
1000
868
|
"example": "SELECT amount FROM rows WHERE (region, amount) = ('west', 10)"
|
|
1001
869
|
},
|
|
1002
870
|
{
|
|
1003
871
|
"id": "predicate.row-in",
|
|
1004
|
-
"feature": "F641",
|
|
1005
872
|
"status": "supported",
|
|
1006
873
|
"example": "SELECT amount FROM rows WHERE (region, amount) IN (('west', 10), ('east', 3))"
|
|
1007
874
|
},
|
|
1008
875
|
{
|
|
1009
876
|
"id": "predicate.row-null",
|
|
1010
|
-
"feature": "F641",
|
|
1011
877
|
"status": "supported",
|
|
1012
878
|
"example": "SELECT amount FROM rows WHERE (region, region) IS NOT NULL"
|
|
1013
879
|
},
|
|
1014
880
|
{
|
|
1015
881
|
"id": "literal.radix",
|
|
1016
|
-
"feature": "T661",
|
|
1017
882
|
"status": "supported",
|
|
1018
883
|
"example": "SELECT 0x0A AS ten"
|
|
1019
884
|
},
|
|
1020
885
|
{
|
|
1021
886
|
"id": "literal.digit-separator",
|
|
1022
|
-
"feature": "T662",
|
|
1023
887
|
"status": "supported",
|
|
1024
888
|
"example": "SELECT 1_000 AS thousand"
|
|
1025
889
|
},
|
|
1026
890
|
{
|
|
1027
891
|
"id": "limit.with-ties",
|
|
1028
|
-
"feature": "F866",
|
|
1029
892
|
"status": "supported",
|
|
1030
893
|
"example": "SELECT region FROM rows WHERE region IS NOT NULL ORDER BY region DESC FETCH FIRST 1 ROWS WITH TIES",
|
|
1031
894
|
"notes": "The limit cannot be pushed into a scan, so these plans run unlimited and the ordered result is trimmed."
|
|
1032
895
|
},
|
|
1033
896
|
{
|
|
1034
897
|
"id": "cte.in-subquery",
|
|
1035
|
-
"feature": "T122",
|
|
1036
898
|
"status": "supported",
|
|
1037
899
|
"example": "SELECT s.amount AS amount FROM (WITH inner_cte AS (SELECT amount FROM rows) SELECT amount FROM inner_cte) s"
|
|
1038
900
|
},
|
|
1039
901
|
{
|
|
1040
902
|
"id": "window.nth-value",
|
|
1041
|
-
"feature": "T618",
|
|
1042
903
|
"status": "supported",
|
|
1043
904
|
"example": "SELECT NTH_VALUE(amount, 2) OVER (ORDER BY amount) AS second FROM rows"
|
|
1044
905
|
},
|
|
1045
906
|
{
|
|
1046
907
|
"id": "window.named",
|
|
1047
|
-
"feature": "T620",
|
|
1048
908
|
"status": "supported",
|
|
1049
909
|
"example": "SELECT SUM(amount) OVER w AS running FROM rows WINDOW w AS (ORDER BY amount)"
|
|
1050
910
|
},
|
|
1051
911
|
{
|
|
1052
912
|
"id": "window.frame-groups",
|
|
1053
|
-
"feature": "T612",
|
|
1054
913
|
"status": "supported",
|
|
1055
914
|
"example": "SELECT COUNT(*) OVER (ORDER BY amount GROUPS BETWEEN 1 PRECEDING AND CURRENT ROW) AS peers FROM rows"
|
|
1056
915
|
},
|
|
1057
916
|
{
|
|
1058
917
|
"id": "window.frame-exclude",
|
|
1059
|
-
"feature": "T612",
|
|
1060
918
|
"status": "supported",
|
|
1061
919
|
"example": "SELECT COUNT(*) OVER (ORDER BY amount RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW) AS others FROM rows"
|
|
1062
920
|
},
|
|
1063
921
|
{
|
|
1064
922
|
"id": "aggregate.grouping",
|
|
1065
|
-
"feature": "T433",
|
|
1066
923
|
"status": "supported",
|
|
1067
924
|
"example": "SELECT GROUPING(region) AS aggregated FROM rows GROUP BY ROLLUP(region)",
|
|
1068
925
|
"notes": "A bitmask over the arguments, most significant first."
|
|
1069
926
|
},
|
|
1070
927
|
{
|
|
1071
928
|
"id": "aggregate.any-value",
|
|
1072
|
-
"feature": "T626",
|
|
1073
929
|
"status": "supported",
|
|
1074
930
|
"example": "SELECT ANY_VALUE(amount) AS sample FROM rows",
|
|
1075
931
|
"notes": "Which row of the group answers is implementation-dependent; this engine returns the minimum."
|
|
1076
932
|
},
|
|
1077
933
|
{
|
|
1078
934
|
"id": "aggregate.variance",
|
|
1079
|
-
"feature": "T621",
|
|
1080
935
|
"status": "supported",
|
|
1081
936
|
"example": "SELECT VAR_POP(amount) AS spread FROM rows",
|
|
1082
937
|
"notes": "Built from COUNT and SUM rather than a dedicated accumulator: the variance is E(x2) - E(x)2. Bare VARIANCE and STDDEV are the sample forms, as in PostgreSQL."
|
|
1083
938
|
},
|
|
1084
939
|
{
|
|
1085
940
|
"id": "aggregate.stddev",
|
|
1086
|
-
"feature": "T621",
|
|
1087
941
|
"status": "supported",
|
|
1088
942
|
"example": "SELECT STDDEV_POP(amount) AS spread FROM rows"
|
|
1089
943
|
},
|
|
1090
944
|
{
|
|
1091
945
|
"id": "aggregate.boolean",
|
|
1092
|
-
"feature": "T631",
|
|
1093
946
|
"status": "supported",
|
|
1094
947
|
"example": "SELECT EVERY(amount > 1) AS all_positive FROM rows"
|
|
1095
948
|
},
|
|
1096
949
|
{
|
|
1097
950
|
"id": "json.value",
|
|
1098
|
-
"feature": "T822",
|
|
1099
951
|
"status": "supported",
|
|
1100
952
|
"example": "SELECT JSON_VALUE('{\"a\": 1}', '$.a') AS a",
|
|
1101
|
-
"notes": "JSON
|
|
953
|
+
"notes": "Accepts JSON text and stored JSON/JSONB values. Paths support $, member steps, and array subscripts."
|
|
1102
954
|
},
|
|
1103
955
|
{
|
|
1104
956
|
"id": "json.query",
|
|
1105
|
-
"feature": "T823",
|
|
1106
957
|
"status": "supported",
|
|
1107
958
|
"example": "SELECT JSON_QUERY('{\"a\": [1, 2]}', '$.a') AS a"
|
|
1108
959
|
},
|
|
1109
960
|
{
|
|
1110
961
|
"id": "json.exists",
|
|
1111
|
-
"feature": "T821",
|
|
1112
962
|
"status": "supported",
|
|
1113
963
|
"example": "SELECT JSON_EXISTS('{\"a\": 1}', '$.a') AS present"
|
|
1114
964
|
},
|
|
1115
965
|
{
|
|
1116
966
|
"id": "json.is-json",
|
|
1117
|
-
"feature": "T825",
|
|
1118
967
|
"status": "supported",
|
|
1119
968
|
"example": "SELECT '{\"a\": 1}' IS JSON OBJECT AS shaped"
|
|
1120
969
|
},
|
|
1121
970
|
{
|
|
1122
971
|
"id": "json.object",
|
|
1123
|
-
"feature": "T811",
|
|
1124
972
|
"status": "supported",
|
|
1125
|
-
"example": "SELECT JSON_OBJECT('a' VALUE 1) AS document"
|
|
973
|
+
"example": "SELECT JSON_OBJECT('a' VALUE 1, 'missing' VALUE NULL) AS document",
|
|
974
|
+
"notes": "Defaults to NULL ON NULL and WITHOUT UNIQUE KEYS. NULL keys are rejected. The constructor returns JSON text; cast or store it as JSON/JSONB for domain validation and JSONB canonicalization."
|
|
1126
975
|
},
|
|
1127
976
|
{
|
|
1128
977
|
"id": "json.array",
|
|
1129
|
-
"feature": "T812",
|
|
1130
978
|
"status": "supported",
|
|
1131
|
-
"example": "SELECT JSON_ARRAY(1, 2) AS document"
|
|
979
|
+
"example": "SELECT JSON_ARRAY(1, NULL, 2) AS document",
|
|
980
|
+
"notes": "Defaults to NULL ON NULL. The explicit NULL/ABSENT clause is not supported. The constructor returns JSON text; cast or store it as JSON/JSONB for domain validation and JSONB canonicalization."
|
|
1132
981
|
},
|
|
1133
982
|
{
|
|
1134
983
|
"id": "ddl.create-table-if-not-exists",
|
|
1135
|
-
"feature": "F031-01",
|
|
1136
984
|
"status": "supported",
|
|
1137
985
|
"example": "CREATE TABLE IF NOT EXISTS made (a INTEGER)"
|
|
1138
986
|
},
|
|
1139
987
|
{
|
|
1140
988
|
"id": "ddl.create-table-default",
|
|
1141
|
-
"feature": "E141-07",
|
|
1142
989
|
"status": "supported",
|
|
1143
990
|
"example": "CREATE TABLE defaulted (id INTEGER PRIMARY KEY, tier TEXT DEFAULT 'basic')",
|
|
1144
|
-
"notes": "
|
|
991
|
+
"notes": "DEFAULT accepts a variable-free scalar expression. Omission or SQL DEFAULT invokes it; explicit NULL follows the column's independent nullability."
|
|
1145
992
|
},
|
|
1146
993
|
{
|
|
1147
994
|
"id": "ddl.create-table-key-clause",
|
|
1148
|
-
"feature": "E141-08",
|
|
1149
995
|
"status": "supported",
|
|
1150
996
|
"example": "CREATE TABLE keyed_clause (a INTEGER, b TEXT, PRIMARY KEY (a))"
|
|
1151
997
|
},
|
|
1152
998
|
{
|
|
1153
999
|
"id": "ddl.alter-table-add-column",
|
|
1154
|
-
"feature": "F031-04",
|
|
1155
1000
|
"status": "supported",
|
|
1156
1001
|
"example": "ALTER TABLE rows ADD COLUMN note TEXT",
|
|
1157
1002
|
"notes": "Existing rows have no value for the new column, so it is always nullable."
|
|
1158
1003
|
},
|
|
1159
1004
|
{
|
|
1160
1005
|
"id": "ddl.create-table-as-select",
|
|
1161
|
-
"feature": "T172",
|
|
1162
1006
|
"status": "supported",
|
|
1163
1007
|
"example": "CREATE TABLE copied AS SELECT region FROM rows"
|
|
1164
1008
|
},
|
|
1009
|
+
{
|
|
1010
|
+
"id": "type.exact-numeric",
|
|
1011
|
+
"status": "supported",
|
|
1012
|
+
"example": "SELECT CAST(1.25 AS DECIMAL(12, 2)) AS amount",
|
|
1013
|
+
"notes": "Exact through storage, arithmetic, comparison, aggregates, and windows. Division retains 20 fractional digits."
|
|
1014
|
+
},
|
|
1015
|
+
{
|
|
1016
|
+
"id": "type.json-jsonb",
|
|
1017
|
+
"status": "supported",
|
|
1018
|
+
"example": "SELECT CAST('{\"a\":1}' AS JSONB) AS document",
|
|
1019
|
+
"notes": "JSON validates and preserves key order; JSONB canonicalizes object keys. Results use JSON text."
|
|
1020
|
+
},
|
|
1021
|
+
{
|
|
1022
|
+
"id": "type.uuid",
|
|
1023
|
+
"status": "supported",
|
|
1024
|
+
"example": "SELECT CAST('550e8400-e29b-41d4-a716-446655440000' AS UUID) AS id",
|
|
1025
|
+
"notes": "Validated and normalized to canonical lowercase text."
|
|
1026
|
+
},
|
|
1027
|
+
{
|
|
1028
|
+
"id": "type.interval",
|
|
1029
|
+
"status": "supported",
|
|
1030
|
+
"example": "SELECT CAST('1 day' AS INTERVAL) AS span",
|
|
1031
|
+
"notes": "Preserves separate month, day, and microsecond fields. Interval arithmetic is not yet exposed."
|
|
1032
|
+
},
|
|
1033
|
+
{
|
|
1034
|
+
"id": "ddl.enum",
|
|
1035
|
+
"status": "supported",
|
|
1036
|
+
"example": "CREATE TYPE mood AS ENUM ('sad', 'ok')",
|
|
1037
|
+
"notes": "Enum columns validate members and compare in declaration order. ALTER TYPE is not supported."
|
|
1038
|
+
},
|
|
1039
|
+
{
|
|
1040
|
+
"id": "ddl.secondary-index",
|
|
1041
|
+
"status": "supported",
|
|
1042
|
+
"example": "CREATE INDEX keyed_score_idx ON keyed(score)",
|
|
1043
|
+
"notes": "PostgreSQL-compatible CREATE INDEX syntax over durable scalar and composite indexes. Leftmost equality and IN prefixes plus the next range column prune candidates; every predicate is rechecked."
|
|
1044
|
+
},
|
|
1045
|
+
{
|
|
1046
|
+
"id": "ddl.drop-secondary-index",
|
|
1047
|
+
"status": "supported",
|
|
1048
|
+
"setup": ["CREATE INDEX keyed_score_idx ON keyed(score)"],
|
|
1049
|
+
"example": "DROP INDEX keyed_score_idx",
|
|
1050
|
+
"notes": "Index names are catalog-global. IF EXISTS is also supported."
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
"id": "ddl.composite-secondary-index",
|
|
1054
|
+
"status": "supported",
|
|
1055
|
+
"example": "CREATE INDEX keyed_score_bonus_idx ON keyed(score, bonus)",
|
|
1056
|
+
"notes": "Composite keys use a prefix-free, type-preserving tuple encoding with ASC or DESC per component. Planning follows the SQL leftmost-prefix rule."
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
"id": "ddl.unique-secondary-index",
|
|
1060
|
+
"status": "supported",
|
|
1061
|
+
"example": "CREATE UNIQUE INDEX keyed_bonus_idx ON keyed(bonus)",
|
|
1062
|
+
"notes": "UNIQUE membership is enforced atomically across inserts, updates, deletes, upserts, triggers, write scopes, snapshots, and concurrent tabs. Matching PostgreSQL, any NULL component does not conflict."
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
"id": "ddl.multiple-unique-constraints",
|
|
1066
|
+
"status": "supported",
|
|
1067
|
+
"example": "CREATE TABLE multi_unique (id INTEGER PRIMARY KEY, email TEXT UNIQUE)",
|
|
1068
|
+
"notes": "Each table-level or column-level UNIQUE constraint is enforced independently."
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
"id": "ddl.composite-primary-key",
|
|
1072
|
+
"status": "supported",
|
|
1073
|
+
"example": "CREATE TABLE composite_key (shop INTEGER, receipt INTEGER, PRIMARY KEY (shop, receipt))",
|
|
1074
|
+
"notes": "Composite keys use a hidden scalar row locator; primary-key components are immutable."
|
|
1075
|
+
},
|
|
1076
|
+
{
|
|
1077
|
+
"id": "ddl.composite-foreign-key",
|
|
1078
|
+
"status": "supported",
|
|
1079
|
+
"example": "CREATE TABLE composite_child (shop INTEGER, receipt INTEGER, FOREIGN KEY (shop, receipt) REFERENCES composite_key(shop, receipt))",
|
|
1080
|
+
"setup": [
|
|
1081
|
+
"CREATE TABLE composite_key (shop INTEGER, receipt INTEGER, PRIMARY KEY (shop, receipt))"
|
|
1082
|
+
]
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
"id": "ddl.alter-table-drop-column",
|
|
1086
|
+
"status": "supported",
|
|
1087
|
+
"example": "ALTER TABLE keyed DROP COLUMN bonus",
|
|
1088
|
+
"notes": "A metadata-only drop refuses unique keys, checks, foreign keys, triggers, views, and the last column. Stored column blocks remain until compaction rewrites their segments; persisted full-text data for the column is removed atomically with the catalog update. RESTRICT is the default and CASCADE is rejected."
|
|
1089
|
+
},
|
|
1090
|
+
{
|
|
1091
|
+
"id": "mutation.upsert-expression",
|
|
1092
|
+
"status": "supported",
|
|
1093
|
+
"example": "INSERT INTO keyed (name, score) VALUES ('x', 2) ON CONFLICT (name) DO UPDATE SET score = score + EXCLUDED.score",
|
|
1094
|
+
"notes": "Assignments may read the stored target row, the proposed EXCLUDED row, parameters, constants, CASE, and scalar functions. All conflicting updates and fresh inserts execute in one write scope; one statement cannot affect the same existing key twice. Aggregates, windows, subqueries, and conflict-key reassignment are rejected."
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1097
|
+
"id": "mutation.upsert-update-where",
|
|
1098
|
+
"status": "supported",
|
|
1099
|
+
"example": "INSERT INTO keyed (name, score) VALUES ('x', 2) ON CONFLICT (name) DO UPDATE SET score = EXCLUDED.score WHERE EXCLUDED.score > keyed.score",
|
|
1100
|
+
"notes": "A false conflict predicate leaves the existing row unchanged."
|
|
1101
|
+
},
|
|
1102
|
+
{
|
|
1103
|
+
"id": "transaction.savepoint",
|
|
1104
|
+
"status": "supported",
|
|
1105
|
+
"example": "SAVEPOINT line_item",
|
|
1106
|
+
"setup": ["BEGIN"],
|
|
1107
|
+
"notes": "SAVEPOINT, ROLLBACK TO, and RELEASE operate on staged transaction state."
|
|
1108
|
+
},
|
|
1165
1109
|
{
|
|
1166
1110
|
"id": "privileges.grant",
|
|
1167
|
-
"feature": "E081",
|
|
1168
1111
|
"status": "unsupported",
|
|
1169
1112
|
"example": "GRANT SELECT ON rows TO reader",
|
|
1170
1113
|
"error": "Expected SELECT, found GRANT",
|
|
@@ -1172,79 +1115,60 @@
|
|
|
1172
1115
|
},
|
|
1173
1116
|
{
|
|
1174
1117
|
"id": "from.lateral",
|
|
1175
|
-
"
|
|
1176
|
-
"
|
|
1177
|
-
"
|
|
1178
|
-
"error": "LATERAL sources are not supported",
|
|
1179
|
-
"notes": "A lateral source re-executes per row of its left side, which the executors have no operator for."
|
|
1118
|
+
"status": "supported",
|
|
1119
|
+
"example": "SELECT x.amount FROM rows r, LATERAL (SELECT amount FROM dims WHERE dims.region = r.region) x",
|
|
1120
|
+
"notes": "Equality-correlated derived sources become set-at-a-time joins. Correlated grouping, ordering, and LIMIT remain unsupported."
|
|
1180
1121
|
},
|
|
1181
1122
|
{
|
|
1182
|
-
"id": "aggregate.
|
|
1183
|
-
"
|
|
1184
|
-
"
|
|
1185
|
-
"
|
|
1186
|
-
"error": "Unsupported function: LISTAGG",
|
|
1187
|
-
"notes": "Needs an ordered string accumulator in the vectorized group state, which the fixed count/sum/value accumulators cannot hold."
|
|
1123
|
+
"id": "aggregate.string-agg",
|
|
1124
|
+
"status": "supported",
|
|
1125
|
+
"example": "SELECT STRING_AGG(region, ',') AS regions FROM rows",
|
|
1126
|
+
"notes": "DISTINCT, aggregate-local ORDER BY, and bounded spill execution are supported."
|
|
1188
1127
|
},
|
|
1189
1128
|
{
|
|
1190
1129
|
"id": "json.table",
|
|
1191
|
-
"
|
|
1192
|
-
"
|
|
1193
|
-
"
|
|
1194
|
-
"error": "JSON_TABLE is not supported",
|
|
1195
|
-
"notes": "A row-producing operator; the same gap as LATERAL."
|
|
1130
|
+
"status": "supported",
|
|
1131
|
+
"example": "SELECT j.a FROM JSON_TABLE('{\"a\":1}', '$' COLUMNS (a INTEGER PATH '$.a')) AS j",
|
|
1132
|
+
"notes": "Constant documents support $ and $[*] row paths. Correlated documents are not yet supported."
|
|
1196
1133
|
},
|
|
1197
1134
|
{
|
|
1198
1135
|
"id": "predicate.similar-to",
|
|
1199
|
-
"
|
|
1200
|
-
"status": "unsupported",
|
|
1136
|
+
"status": "supported",
|
|
1201
1137
|
"example": "SELECT amount FROM rows WHERE region SIMILAR TO 'w%'",
|
|
1202
|
-
"
|
|
1203
|
-
"notes": "LIKE and the regular-expression-free MATCH cover the same ground for the data sizes this engine targets."
|
|
1138
|
+
"notes": "Whole-string SQL wildcard and regular-expression semantics."
|
|
1204
1139
|
},
|
|
1205
1140
|
{
|
|
1206
1141
|
"id": "collation.explicit",
|
|
1207
|
-
"
|
|
1208
|
-
"
|
|
1209
|
-
"
|
|
1210
|
-
"error": "Expected eof, found COLLATE",
|
|
1211
|
-
"notes": "String comparison is one documented collation; a per-query collation would change index order too."
|
|
1142
|
+
"status": "supported",
|
|
1143
|
+
"example": "SELECT region FROM rows ORDER BY region COLLATE \"C\"",
|
|
1144
|
+
"notes": "C, POSIX, and host Intl locale names are accepted. Collated ordering does not use a plain string index."
|
|
1212
1145
|
},
|
|
1213
1146
|
{
|
|
1214
1147
|
"id": "aggregate.json",
|
|
1215
|
-
"
|
|
1216
|
-
"status": "unsupported",
|
|
1148
|
+
"status": "supported",
|
|
1217
1149
|
"example": "SELECT JSON_ARRAYAGG(region) AS regions FROM rows",
|
|
1218
|
-
"
|
|
1219
|
-
"notes": "Aggregating into a document needs the same ordered accumulator LISTAGG does; JSON_ARRAY builds one from scalars today."
|
|
1150
|
+
"notes": "Supports DISTINCT, includes SQL NULL as JSON null, and returns NULL for empty input. FILTER, window use, aggregate-local ORDER BY, and explicit NULL/ABSENT clauses are not supported; input order is otherwise unspecified."
|
|
1220
1151
|
},
|
|
1221
1152
|
{
|
|
1222
1153
|
"id": "type.array",
|
|
1223
|
-
"
|
|
1224
|
-
"status": "unsupported",
|
|
1154
|
+
"status": "supported",
|
|
1225
1155
|
"example": "SELECT ARRAY[1, 2] AS pair",
|
|
1226
|
-
"
|
|
1227
|
-
"notes": "Array and multiset types would need an encoding and a comparison order the columnar format would carry forever; a JSON document in a text column covers the same shape."
|
|
1156
|
+
"notes": "Constructors and array columns use canonical JSON text at the JavaScript boundary. Subscripts and array operators are not yet supported."
|
|
1228
1157
|
},
|
|
1229
1158
|
{
|
|
1230
1159
|
"id": "type.time",
|
|
1231
|
-
"
|
|
1232
|
-
"status": "unsupported",
|
|
1160
|
+
"status": "supported",
|
|
1233
1161
|
"example": "SELECT TIME '12:00:00' AS at",
|
|
1234
|
-
"
|
|
1235
|
-
"notes": "The engine has four logical types and stores every datetime as an instant in UTC; a bare time of day has no instant. LOCALTIME reads as an 'HH:MM:SS' string instead."
|
|
1162
|
+
"notes": "A time of day without a time zone, returned as canonical text."
|
|
1236
1163
|
},
|
|
1237
1164
|
{
|
|
1238
1165
|
"id": "ddl.sequence",
|
|
1239
|
-
"
|
|
1240
|
-
"status": "unsupported",
|
|
1166
|
+
"status": "supported",
|
|
1241
1167
|
"example": "CREATE SEQUENCE order_ids",
|
|
1242
|
-
"
|
|
1243
|
-
"notes": "A sequence is a second kind of catalog object with its own durability and contention story; the key column's autoincrement default covers the common use."
|
|
1168
|
+
"notes": "NEXTVAL and session-local CURRVAL are supported. Sequence options and ALTER SEQUENCE are not."
|
|
1244
1169
|
},
|
|
1245
1170
|
{
|
|
1246
1171
|
"id": "ddl.drop-table",
|
|
1247
|
-
"feature": "F031-13",
|
|
1248
1172
|
"status": "supported",
|
|
1249
1173
|
"setup": ["CREATE TABLE doomed (a INTEGER)"],
|
|
1250
1174
|
"example": "DROP TABLE doomed",
|
|
@@ -1252,32 +1176,28 @@
|
|
|
1252
1176
|
},
|
|
1253
1177
|
{
|
|
1254
1178
|
"id": "ddl.create-view",
|
|
1255
|
-
"feature": "F031-02",
|
|
1256
1179
|
"status": "supported",
|
|
1257
1180
|
"example": "CREATE VIEW west AS SELECT region, amount FROM rows WHERE region = 'west'",
|
|
1258
1181
|
"notes": "The catalog stores the query text and the schema inferred from it, so a view answers the same questions a table does and reads expand it into the query it stands for — anywhere a read runs, including inside a write scope. A view is never a write target. CREATE OR REPLACE redefines one; a view stacked on it follows the new definition, and a cycle two redefinitions close is caught on the next read rather than recursed."
|
|
1259
1182
|
},
|
|
1260
1183
|
{
|
|
1261
1184
|
"id": "ddl.drop-view",
|
|
1262
|
-
"feature": "F031-16",
|
|
1263
1185
|
"status": "supported",
|
|
1264
1186
|
"setup": ["CREATE VIEW doomed_view AS SELECT amount FROM rows"],
|
|
1265
1187
|
"example": "DROP VIEW doomed_view"
|
|
1266
1188
|
},
|
|
1267
1189
|
{
|
|
1268
1190
|
"id": "ddl.check-constraint",
|
|
1269
|
-
"feature": "E141-06",
|
|
1270
1191
|
"status": "supported",
|
|
1271
1192
|
"example": "CREATE TABLE checked (a INTEGER NOT NULL CHECK (a > 0), CONSTRAINT small CHECK (a < 100))",
|
|
1272
1193
|
"notes": "A row condition over the table's own columns, evaluated by the writer on every path that writes a row — insert, upsert, and update, which is checked against its post-image. A constraint fails only when it evaluates to false, so SQL's unknown passes: NULL satisfies CHECK (a > 0) unless the column is also NOT NULL."
|
|
1273
1194
|
},
|
|
1274
1195
|
{
|
|
1275
1196
|
"id": "ddl.foreign-key",
|
|
1276
|
-
"feature": "E141-04",
|
|
1277
1197
|
"status": "supported",
|
|
1278
1198
|
"setup": ["CREATE TABLE parents (id INTEGER PRIMARY KEY, label TEXT NOT NULL)"],
|
|
1279
1199
|
"example": "CREATE TABLE children (id INTEGER PRIMARY KEY, parent INTEGER REFERENCES parents(id) ON DELETE CASCADE)",
|
|
1280
|
-
"notes": "
|
|
1200
|
+
"notes": "Scalar and composite references target the parent's matching primary-key columns. Writes validate against their transaction; NULL references are satisfied. ON DELETE supports RESTRICT, CASCADE, and SET NULL atomically. Primary keys are immutable, so ON UPDATE has no action."
|
|
1281
1201
|
}
|
|
1282
1202
|
]
|
|
1283
1203
|
}
|