@minnowdb/core 0.2.0 → 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 +3 -0
- package/dist/engine/client.d.ts.map +1 -1
- package/dist/engine/client.js +21 -10
- package/dist/engine/client.js.map +1 -1
- package/dist/engine/database.d.ts +71 -5
- package/dist/engine/database.d.ts.map +1 -1
- package/dist/engine/database.js +3619 -421
- 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 +6 -2
- 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 +132 -22
- package/dist/engine/query.d.ts.map +1 -1
- package/dist/engine/query.js +1368 -246
- 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 +8 -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 +179 -251
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,509 +85,456 @@
|
|
|
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
|
},
|
|
337
|
+
{
|
|
338
|
+
"id": "predicate.match-parameter",
|
|
339
|
+
"status": "supported",
|
|
340
|
+
"example": "SELECT region FROM rows WHERE MATCH(region) AGAINST $1 ORDER BY BM25(region) AGAINST $1 DESC",
|
|
341
|
+
"params": ["west"],
|
|
342
|
+
"notes": "Search text binds like any other value, so search-as-you-type can reuse one planned statement."
|
|
343
|
+
},
|
|
374
344
|
{
|
|
375
345
|
"id": "function.bm25",
|
|
376
|
-
"feature": "minnow",
|
|
377
346
|
"status": "supported",
|
|
378
347
|
"example": "SELECT region, BM25(region) AGAINST 'west' AS score FROM rows WHERE MATCH(region) AGAINST 'west' ORDER BY score DESC"
|
|
379
348
|
},
|
|
380
349
|
{
|
|
381
350
|
"id": "order-by.expression",
|
|
382
|
-
"feature": "E121",
|
|
383
351
|
"status": "supported",
|
|
384
352
|
"example": "SELECT region FROM rows WHERE amount > 0 ORDER BY amount * 2 DESC, region"
|
|
385
353
|
},
|
|
386
354
|
{
|
|
387
355
|
"id": "where.between",
|
|
388
|
-
"feature": "E061-02",
|
|
389
356
|
"status": "supported",
|
|
390
357
|
"example": "SELECT region FROM rows WHERE amount BETWEEN 1 AND 5"
|
|
391
358
|
},
|
|
392
359
|
{
|
|
393
360
|
"id": "where.between-symmetric",
|
|
394
|
-
"feature": "T461",
|
|
395
361
|
"status": "supported",
|
|
396
362
|
"example": "SELECT region FROM rows WHERE amount BETWEEN SYMMETRIC 5 AND 1",
|
|
397
363
|
"notes": "SYMMETRIC accepts the bounds in either order."
|
|
398
364
|
},
|
|
399
365
|
{
|
|
400
366
|
"id": "where.is-null",
|
|
401
|
-
"feature": "E061-06",
|
|
402
367
|
"status": "supported",
|
|
403
368
|
"example": "SELECT region FROM rows WHERE region IS NULL"
|
|
404
369
|
},
|
|
405
370
|
{
|
|
406
371
|
"id": "where.is-not-null",
|
|
407
|
-
"feature": "E061-06",
|
|
408
372
|
"status": "supported",
|
|
409
373
|
"example": "SELECT amount FROM rows WHERE region IS NOT NULL"
|
|
410
374
|
},
|
|
411
375
|
{
|
|
412
376
|
"id": "where.exists",
|
|
413
|
-
"feature": "E061-08",
|
|
414
377
|
"status": "supported",
|
|
415
378
|
"example": "SELECT region FROM rows WHERE EXISTS (SELECT 1 FROM dims)",
|
|
416
|
-
"notes": "
|
|
379
|
+
"notes": "Both uncorrelated and correlated EXISTS are supported. Correlated forms are decorrelated into joins rather than executed once per outer row."
|
|
417
380
|
},
|
|
418
381
|
{
|
|
419
382
|
"id": "expression.case",
|
|
420
|
-
"feature": "F261-02",
|
|
421
383
|
"status": "supported",
|
|
422
384
|
"example": "SELECT CASE WHEN amount > 5 THEN 'big' ELSE 'small' END AS size FROM rows"
|
|
423
385
|
},
|
|
424
386
|
{
|
|
425
387
|
"id": "subquery.correlated",
|
|
426
|
-
"feature": "E061-13",
|
|
427
388
|
"status": "supported",
|
|
428
389
|
"example": "SELECT region FROM rows r WHERE amount > (SELECT AVG(amount) FROM rows q WHERE q.region = r.region)",
|
|
429
390
|
"notes": "Equality-correlated subqueries decorrelate into derived-table joins at compile time; both executors run plain joins."
|
|
430
391
|
},
|
|
431
392
|
{
|
|
432
393
|
"id": "subquery.correlated-exists",
|
|
433
|
-
"feature": "E061-13",
|
|
434
394
|
"status": "supported",
|
|
435
395
|
"example": "SELECT amount FROM rows r WHERE EXISTS (SELECT region FROM dims d WHERE d.region = r.region)",
|
|
436
396
|
"notes": "EXISTS joins the subquery's distinct correlation keys; NOT EXISTS becomes a left join checked with IS NULL."
|
|
437
397
|
},
|
|
438
398
|
{
|
|
439
399
|
"id": "subquery.correlated-select",
|
|
440
|
-
"feature": "E061-13",
|
|
441
400
|
"status": "supported",
|
|
442
401
|
"example": "SELECT r.region, (SELECT AVG(q.amount) FROM rows q WHERE q.region = r.region) AS regional FROM rows r",
|
|
443
402
|
"notes": "Correlated scalar aggregates decorrelate in the select list too, outside grouped queries."
|
|
444
403
|
},
|
|
445
404
|
{
|
|
446
405
|
"id": "subquery.correlated-non-equi",
|
|
447
|
-
"
|
|
448
|
-
"
|
|
449
|
-
"
|
|
450
|
-
"error": "support only equality conditions",
|
|
451
|
-
"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."
|
|
452
409
|
},
|
|
453
410
|
{
|
|
454
411
|
"id": "subquery.correlated-not-in",
|
|
455
|
-
"
|
|
456
|
-
"
|
|
457
|
-
"example": "SELECT region FROM rows r WHERE region NOT IN (SELECT d.region FROM dims d WHERE d.region = r.region)",
|
|
458
|
-
"error": "use NOT EXISTS",
|
|
459
|
-
"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)"
|
|
460
414
|
},
|
|
461
415
|
{
|
|
462
416
|
"id": "cte.recursive",
|
|
463
|
-
"feature": "T131",
|
|
464
417
|
"status": "supported",
|
|
465
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",
|
|
466
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."
|
|
467
420
|
},
|
|
468
421
|
{
|
|
469
422
|
"id": "mutation.with-cte",
|
|
470
|
-
"feature": "T121",
|
|
471
423
|
"status": "supported",
|
|
472
424
|
"example": "WITH totals AS (SELECT MAX(score) AS top FROM keyed) DELETE FROM keyed WHERE score >= (SELECT top FROM totals) RETURNING name",
|
|
473
425
|
"notes": "WITH precedes INSERT/UPDATE/DELETE; the CTEs are visible to the statement's queries and subqueries."
|
|
474
426
|
},
|
|
475
427
|
{
|
|
476
428
|
"id": "set.intersect",
|
|
477
|
-
"feature": "F302-01",
|
|
478
429
|
"status": "supported",
|
|
479
430
|
"example": "SELECT region FROM rows INTERSECT SELECT region FROM dims",
|
|
480
|
-
"notes": "INTERSECT binds tighter than UNION and EXCEPT,
|
|
431
|
+
"notes": "INTERSECT binds tighter than UNION and EXCEPT, matching PostgreSQL."
|
|
481
432
|
},
|
|
482
433
|
{
|
|
483
434
|
"id": "set.except",
|
|
484
|
-
"feature": "E071-03",
|
|
485
435
|
"status": "supported",
|
|
486
436
|
"example": "SELECT region FROM rows EXCEPT SELECT region FROM dims"
|
|
487
437
|
},
|
|
488
438
|
{
|
|
489
439
|
"id": "set.intersect-all",
|
|
490
|
-
"feature": "F302-02",
|
|
491
440
|
"status": "supported",
|
|
492
441
|
"example": "SELECT region FROM rows INTERSECT ALL SELECT region FROM dims",
|
|
493
442
|
"notes": "Bag semantics; SQLite itself has no INTERSECT ALL."
|
|
494
443
|
},
|
|
495
444
|
{
|
|
496
445
|
"id": "set.except-all",
|
|
497
|
-
"feature": "F304",
|
|
498
446
|
"status": "supported",
|
|
499
447
|
"example": "SELECT region FROM rows EXCEPT ALL SELECT region FROM dims",
|
|
500
448
|
"notes": "Bag semantics; SQLite itself has no EXCEPT ALL."
|
|
501
449
|
},
|
|
502
450
|
{
|
|
503
451
|
"id": "aggregate.count-distinct",
|
|
504
|
-
"feature": "E091-07",
|
|
505
452
|
"status": "supported",
|
|
506
453
|
"example": "SELECT COUNT(DISTINCT region) AS regions FROM rows"
|
|
507
454
|
},
|
|
508
455
|
{
|
|
509
456
|
"id": "aggregate.filter",
|
|
510
|
-
"feature": "T612",
|
|
511
457
|
"status": "supported",
|
|
512
458
|
"example": "SELECT region, COUNT(*) FILTER (WHERE amount > 5) AS big FROM rows GROUP BY region",
|
|
513
459
|
"notes": "Desugars into a CASE inside the aggregate, so it works with every aggregate and DISTINCT."
|
|
514
460
|
},
|
|
515
461
|
{
|
|
516
462
|
"id": "window.aggregate-over",
|
|
517
|
-
"feature": "T611",
|
|
518
463
|
"status": "supported",
|
|
519
464
|
"example": "SELECT SUM(amount) OVER (PARTITION BY region) AS total FROM rows",
|
|
520
465
|
"notes": "Default frame only: whole partition without OVER ordering, running with peers when ordered."
|
|
521
466
|
},
|
|
522
467
|
{
|
|
523
468
|
"id": "window.in-expression",
|
|
524
|
-
"feature": "T611",
|
|
525
469
|
"status": "supported",
|
|
526
470
|
"example": "SELECT amount, amount - LAG(amount) OVER (ORDER BY amount, region) AS change, 100.0 * amount / SUM(amount) OVER () AS pct FROM rows",
|
|
527
471
|
"notes": "A window is an expression: the arithmetic around it is evaluated after the window has run, over the column it produced."
|
|
528
472
|
},
|
|
529
473
|
{
|
|
530
474
|
"id": "window.over-grouped",
|
|
531
|
-
"feature": "T611",
|
|
532
475
|
"status": "supported",
|
|
533
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",
|
|
534
|
-
"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."
|
|
535
478
|
},
|
|
536
479
|
{
|
|
537
480
|
"id": "window.value-functions",
|
|
538
|
-
"feature": "T617",
|
|
539
481
|
"status": "supported",
|
|
540
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",
|
|
541
|
-
"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."
|
|
542
484
|
},
|
|
543
485
|
{
|
|
544
486
|
"id": "window.ntile",
|
|
545
|
-
"feature": "T614",
|
|
546
487
|
"status": "supported",
|
|
547
488
|
"example": "SELECT amount, NTILE(2) OVER (ORDER BY amount) AS half FROM rows"
|
|
548
489
|
},
|
|
549
490
|
{
|
|
550
491
|
"id": "window.distribution",
|
|
551
|
-
"feature": "T612",
|
|
552
492
|
"status": "supported",
|
|
553
493
|
"example": "SELECT amount, PERCENT_RANK() OVER (ORDER BY amount) AS pr, CUME_DIST() OVER (ORDER BY amount) AS cd FROM rows"
|
|
554
494
|
},
|
|
555
495
|
{
|
|
556
496
|
"id": "window.frame",
|
|
557
|
-
"feature": "T612",
|
|
558
497
|
"status": "supported",
|
|
559
498
|
"example": "SELECT amount, SUM(amount) OVER (ORDER BY amount, joined ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS windowed FROM rows",
|
|
560
499
|
"notes": "ROWS frames take row-distance bounds; RANGE frames take UNBOUNDED and CURRENT ROW bounds, where CURRENT ROW spans the ordering peer group."
|
|
561
500
|
},
|
|
562
501
|
{
|
|
563
502
|
"id": "join.right",
|
|
564
|
-
"feature": "F041-04",
|
|
565
503
|
"status": "supported",
|
|
566
504
|
"example": "SELECT r.region FROM rows r RIGHT JOIN dims d ON d.region = r.region",
|
|
567
505
|
"notes": "Desugars to the mirrored LEFT JOIN; supported as the sole join of a block."
|
|
568
506
|
},
|
|
569
507
|
{
|
|
570
508
|
"id": "join.non-equi",
|
|
571
|
-
"feature": "F041-08",
|
|
572
509
|
"status": "supported",
|
|
573
510
|
"example": "SELECT r.region FROM rows r JOIN dims d ON d.amount > r.amount",
|
|
574
511
|
"notes": "Executes as a nested-loop join (probe x build); equalities keep the hash path."
|
|
575
512
|
},
|
|
576
513
|
{
|
|
577
514
|
"id": "select.distinct-wildcard",
|
|
578
|
-
"feature": "E051-01",
|
|
579
515
|
"status": "supported",
|
|
580
516
|
"example": "SELECT DISTINCT * FROM rows",
|
|
581
517
|
"notes": "Expands to DISTINCT over every wildcard output column once input schemas are known."
|
|
582
518
|
},
|
|
583
519
|
{
|
|
584
520
|
"id": "limit.offset",
|
|
585
|
-
"feature": "F856",
|
|
586
521
|
"status": "supported",
|
|
587
522
|
"example": "SELECT amount FROM rows LIMIT 5 OFFSET 2",
|
|
588
523
|
"notes": "OFFSET is accepted directly after LIMIT."
|
|
589
524
|
},
|
|
590
525
|
{
|
|
591
526
|
"id": "select.no-from",
|
|
592
|
-
"feature": "E051",
|
|
593
527
|
"status": "supported",
|
|
594
528
|
"example": "SELECT 1 + 1 AS two, UPPER('minnow') AS name"
|
|
595
529
|
},
|
|
596
530
|
{
|
|
597
531
|
"id": "select.values",
|
|
598
|
-
"feature": "F641",
|
|
599
532
|
"status": "supported",
|
|
600
533
|
"example": "SELECT v.column1 AS n, v.column2 AS tag FROM (VALUES (1, 'one'), (2, 'two')) v",
|
|
601
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."
|
|
602
535
|
},
|
|
603
536
|
{
|
|
604
537
|
"id": "limit.parameter",
|
|
605
|
-
"feature": "F865",
|
|
606
538
|
"status": "supported",
|
|
607
539
|
"example": "SELECT region, amount FROM rows ORDER BY region NULLS LAST, amount LIMIT $1 OFFSET $2",
|
|
608
540
|
"params": [2, 1],
|
|
@@ -610,28 +542,24 @@
|
|
|
610
542
|
},
|
|
611
543
|
{
|
|
612
544
|
"id": "limit.fetch-first",
|
|
613
|
-
"feature": "F856",
|
|
614
545
|
"status": "supported",
|
|
615
546
|
"example": "SELECT amount FROM rows ORDER BY amount OFFSET 1 ROWS FETCH FIRST 2 ROWS ONLY",
|
|
616
|
-
"notes": "
|
|
547
|
+
"notes": "PostgreSQL's FETCH clause is accepted as a spelling of LIMIT; SQLite itself only speaks LIMIT."
|
|
617
548
|
},
|
|
618
549
|
{
|
|
619
550
|
"id": "offset.standalone",
|
|
620
|
-
"feature": "F856",
|
|
621
551
|
"status": "supported",
|
|
622
552
|
"example": "SELECT amount FROM rows ORDER BY amount OFFSET 2",
|
|
623
553
|
"notes": "OFFSET no longer requires LIMIT. SQLite itself needs LIMIT -1 OFFSET n."
|
|
624
554
|
},
|
|
625
555
|
{
|
|
626
556
|
"id": "ddl.create-table",
|
|
627
|
-
"feature": "F031-01",
|
|
628
557
|
"status": "supported",
|
|
629
558
|
"example": "CREATE TABLE made (id INTEGER PRIMARY KEY, label TEXT NOT NULL, at TIMESTAMP)",
|
|
630
|
-
"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."
|
|
631
560
|
},
|
|
632
561
|
{
|
|
633
562
|
"id": "mutation.update-keyless",
|
|
634
|
-
"feature": "E101-03",
|
|
635
563
|
"status": "unsupported",
|
|
636
564
|
"example": "UPDATE rows SET amount = 1",
|
|
637
565
|
"error": "UPDATE requires a table with a unique key",
|
|
@@ -639,21 +567,18 @@
|
|
|
639
567
|
},
|
|
640
568
|
{
|
|
641
569
|
"id": "trigger.create-after",
|
|
642
|
-
"feature": "T211",
|
|
643
570
|
"status": "supported",
|
|
644
571
|
"example": "CREATE TRIGGER keyed_audit AFTER INSERT ON keyed BEGIN INSERT INTO rows (region, amount) VALUES (NEW.name, NEW.score); END",
|
|
645
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."
|
|
646
573
|
},
|
|
647
574
|
{
|
|
648
575
|
"id": "trigger.create-before",
|
|
649
|
-
"feature": "T211",
|
|
650
576
|
"status": "supported",
|
|
651
577
|
"example": "CREATE TRIGGER keyed_before BEFORE INSERT ON keyed BEGIN INSERT INTO rows (region, amount) VALUES (NEW.name, NEW.score); END",
|
|
652
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."
|
|
653
579
|
},
|
|
654
580
|
{
|
|
655
581
|
"id": "trigger.body-update-delete",
|
|
656
|
-
"feature": "T211",
|
|
657
582
|
"status": "supported",
|
|
658
583
|
"example": "CREATE TRIGGER keyed_counts AFTER INSERT ON keyed BEGIN UPDATE stats SET total = total + NEW.score WHERE region = NEW.name; END",
|
|
659
584
|
"setup": [
|
|
@@ -664,7 +589,6 @@
|
|
|
664
589
|
},
|
|
665
590
|
{
|
|
666
591
|
"id": "trigger.drop",
|
|
667
|
-
"feature": "T211",
|
|
668
592
|
"status": "supported",
|
|
669
593
|
"example": "DROP TRIGGER droppable_audit",
|
|
670
594
|
"setup": [
|
|
@@ -673,185 +597,158 @@
|
|
|
673
597
|
},
|
|
674
598
|
{
|
|
675
599
|
"id": "where.parenthesized",
|
|
676
|
-
"feature": "E061-14",
|
|
677
600
|
"status": "supported",
|
|
678
601
|
"example": "SELECT region FROM rows WHERE (amount > 5 AND region = 'west')"
|
|
679
602
|
},
|
|
680
603
|
{
|
|
681
604
|
"id": "where.not",
|
|
682
|
-
"feature": "E061-14",
|
|
683
605
|
"status": "supported",
|
|
684
606
|
"example": "SELECT region FROM rows WHERE NOT active = TRUE"
|
|
685
607
|
},
|
|
686
608
|
{
|
|
687
609
|
"id": "expression.concat",
|
|
688
|
-
"feature": "E021-07",
|
|
689
610
|
"status": "supported",
|
|
690
611
|
"example": "SELECT region || '-' || label AS tag FROM dims",
|
|
691
612
|
"notes": "|| concatenates strings and propagates NULL; non-string operands are a type error."
|
|
692
613
|
},
|
|
693
614
|
{
|
|
694
615
|
"id": "expression.modulo",
|
|
695
|
-
"feature": "T441",
|
|
696
616
|
"status": "supported",
|
|
697
617
|
"example": "SELECT amount % 3 AS remainder FROM rows",
|
|
698
618
|
"notes": "Division and remainder by zero are NULL, matching SQLite."
|
|
699
619
|
},
|
|
700
620
|
{
|
|
701
621
|
"id": "expression.cast",
|
|
702
|
-
"feature": "F201",
|
|
703
622
|
"status": "supported",
|
|
704
623
|
"example": "SELECT CAST(amount AS INTEGER) AS whole, CAST(amount AS TEXT) AS label FROM rows",
|
|
705
|
-
"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."
|
|
706
625
|
},
|
|
707
626
|
{
|
|
708
627
|
"id": "identifier.quoted",
|
|
709
|
-
"feature": "E031-01",
|
|
710
628
|
"status": "supported",
|
|
711
629
|
"example": "SELECT \"region\", \"rows\".\"amount\" FROM \"rows\" WHERE \"amount\" > 5",
|
|
712
630
|
"notes": "Double-quoted identifiers are never keywords and keep their exact spelling."
|
|
713
631
|
},
|
|
714
632
|
{
|
|
715
633
|
"id": "order-by.nulls",
|
|
716
|
-
"feature": "T611",
|
|
717
634
|
"status": "supported",
|
|
718
635
|
"example": "SELECT region, amount FROM rows ORDER BY region NULLS LAST, amount",
|
|
719
|
-
"notes": "Without NULLS FIRST/LAST the default
|
|
636
|
+
"notes": "Without NULLS FIRST/LAST the default follows PostgreSQL: NULLs last ascending, first descending."
|
|
720
637
|
},
|
|
721
638
|
{
|
|
722
639
|
"id": "expression.coalesce",
|
|
723
|
-
"feature": "F261-04",
|
|
724
640
|
"status": "supported",
|
|
725
641
|
"example": "SELECT COALESCE(region, 'unknown') AS region_label FROM rows",
|
|
726
642
|
"notes": "Arguments evaluate left to right; the first non-NULL value wins. All non-NULL arguments must share one type."
|
|
727
643
|
},
|
|
728
644
|
{
|
|
729
645
|
"id": "expression.date-trunc",
|
|
730
|
-
"feature": "minnow",
|
|
731
646
|
"status": "supported",
|
|
732
647
|
"example": "SELECT DATE_TRUNC('month', joined) AS joined_month FROM rows",
|
|
733
648
|
"notes": "Units: year, quarter, month, week (Monday start), day, hour, minute, second. Truncation is in UTC; the engine has no session time zone."
|
|
734
649
|
},
|
|
735
650
|
{
|
|
736
651
|
"id": "expression.date-add",
|
|
737
|
-
"feature": "F052",
|
|
738
652
|
"status": "supported",
|
|
739
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",
|
|
740
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."
|
|
741
655
|
},
|
|
742
656
|
{
|
|
743
657
|
"id": "function.string-core",
|
|
744
|
-
"feature": "E021-08",
|
|
745
658
|
"status": "supported",
|
|
746
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",
|
|
747
660
|
"notes": "SUBSTRING is accepted as a spelling of SUBSTR; LENGTH and SUBSTR count characters, not UTF-16 units."
|
|
748
661
|
},
|
|
749
662
|
{
|
|
750
663
|
"id": "function.abs",
|
|
751
|
-
"feature": "T441",
|
|
752
664
|
"status": "supported",
|
|
753
665
|
"example": "SELECT ABS(amount - 5) AS distance FROM rows"
|
|
754
666
|
},
|
|
755
667
|
{
|
|
756
668
|
"id": "function.numeric-core",
|
|
757
|
-
"feature": "T441",
|
|
758
669
|
"status": "supported",
|
|
759
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",
|
|
760
671
|
"notes": "GREATEST/LEAST ignore NULL arguments, matching PostgreSQL."
|
|
761
672
|
},
|
|
762
673
|
{
|
|
763
674
|
"id": "function.string-extended",
|
|
764
|
-
"feature": "E021-06",
|
|
765
675
|
"status": "supported",
|
|
766
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"
|
|
767
677
|
},
|
|
768
678
|
{
|
|
769
679
|
"id": "function.extract",
|
|
770
|
-
"feature": "F052",
|
|
771
680
|
"status": "supported",
|
|
772
681
|
"example": "SELECT EXTRACT(year FROM joined) AS y, EXTRACT(dow FROM joined) AS d FROM rows WHERE joined IS NOT NULL",
|
|
773
682
|
"notes": "Fields: year, quarter, month, week (ISO), day, hour, minute, second, epoch, dow — all in UTC. SQLite spells this strftime."
|
|
774
683
|
},
|
|
775
684
|
{
|
|
776
685
|
"id": "aggregate.distinct-argument",
|
|
777
|
-
"feature": "E091-07",
|
|
778
686
|
"status": "supported",
|
|
779
687
|
"example": "SELECT region, COUNT(DISTINCT amount) AS amounts, COUNT(DISTINCT active) AS states, SUM(amount) AS total FROM rows GROUP BY region",
|
|
780
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."
|
|
781
689
|
},
|
|
782
690
|
{
|
|
783
691
|
"id": "join.multi-key",
|
|
784
|
-
"feature": "F041-01",
|
|
785
692
|
"status": "supported",
|
|
786
693
|
"example": "SELECT r.region FROM rows r JOIN dims d ON d.region = r.region AND d.amount = r.amount",
|
|
787
694
|
"notes": "Multi-key conditions execute as a nested-loop join; single equalities keep the hash path."
|
|
788
695
|
},
|
|
789
696
|
{
|
|
790
697
|
"id": "join.cross",
|
|
791
|
-
"feature": "F401-04",
|
|
792
698
|
"status": "supported",
|
|
793
699
|
"example": "SELECT r.region AS region, d.label AS label FROM rows r CROSS JOIN dims d"
|
|
794
700
|
},
|
|
795
701
|
{
|
|
796
702
|
"id": "join.full",
|
|
797
|
-
"feature": "F401-02",
|
|
798
703
|
"status": "supported",
|
|
799
704
|
"example": "SELECT r.amount AS amount, d.label AS label FROM rows r FULL JOIN dims d ON d.region = r.region",
|
|
800
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."
|
|
801
706
|
},
|
|
802
707
|
{
|
|
803
708
|
"id": "order-by.ordinal",
|
|
804
|
-
"feature": "E121",
|
|
805
709
|
"status": "supported",
|
|
806
710
|
"example": "SELECT region, amount FROM rows ORDER BY 2 DESC",
|
|
807
711
|
"notes": "Ordinals resolve to the select list at compile time; out-of-range ordinals are an error."
|
|
808
712
|
},
|
|
809
713
|
{
|
|
810
714
|
"id": "window.lag-lead",
|
|
811
|
-
"feature": "T615",
|
|
812
715
|
"status": "supported",
|
|
813
716
|
"example": "SELECT amount, LAG(amount) OVER (ORDER BY amount) AS previous, LEAD(amount, 1, -1) OVER (ORDER BY amount) AS next FROM rows",
|
|
814
717
|
"notes": "LAG/LEAD take a constant offset (default 1) and default value (default NULL), and require ORDER BY inside OVER."
|
|
815
718
|
},
|
|
816
719
|
{
|
|
817
720
|
"id": "mutation.insert-select",
|
|
818
|
-
"feature": "E101-01",
|
|
819
721
|
"status": "supported",
|
|
820
722
|
"example": "INSERT INTO keyed (name, score) SELECT name || '2' AS name, score + 1 AS score FROM keyed",
|
|
821
723
|
"notes": "The SELECT runs at one snapshot and materializes before the batch write."
|
|
822
724
|
},
|
|
823
725
|
{
|
|
824
726
|
"id": "mutation.merge",
|
|
825
|
-
"feature": "F312",
|
|
826
727
|
"status": "supported",
|
|
827
728
|
"setup": ["INSERT INTO keyed (name, score, bonus) VALUES ('z', 5, NULL)"],
|
|
828
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)",
|
|
829
|
-
"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."
|
|
830
731
|
},
|
|
831
732
|
{
|
|
832
733
|
"id": "transaction.begin",
|
|
833
|
-
"feature": "E151-01",
|
|
834
734
|
"status": "supported",
|
|
835
735
|
"example": "BEGIN",
|
|
836
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."
|
|
837
737
|
},
|
|
838
738
|
{
|
|
839
739
|
"id": "transaction.commit",
|
|
840
|
-
"feature": "E151-01",
|
|
841
740
|
"status": "supported",
|
|
842
741
|
"setup": ["BEGIN"],
|
|
843
742
|
"example": "COMMIT"
|
|
844
743
|
},
|
|
845
744
|
{
|
|
846
745
|
"id": "transaction.rollback",
|
|
847
|
-
"feature": "E151-02",
|
|
848
746
|
"status": "supported",
|
|
849
747
|
"setup": ["BEGIN"],
|
|
850
748
|
"example": "ROLLBACK"
|
|
851
749
|
},
|
|
852
750
|
{
|
|
853
751
|
"id": "transaction.isolation-level",
|
|
854
|
-
"feature": "E152-01",
|
|
855
752
|
"status": "unsupported",
|
|
856
753
|
"example": "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE",
|
|
857
754
|
"error": "Expected SELECT, found SET",
|
|
@@ -859,304 +756,358 @@
|
|
|
859
756
|
},
|
|
860
757
|
{
|
|
861
758
|
"id": "function.char-length",
|
|
862
|
-
"feature": "E021-04",
|
|
863
759
|
"status": "supported",
|
|
864
760
|
"example": "SELECT CHAR_LENGTH(region) AS n FROM rows WHERE region IS NOT NULL"
|
|
865
761
|
},
|
|
866
762
|
{
|
|
867
763
|
"id": "function.octet-length",
|
|
868
|
-
"feature": "E021-05",
|
|
869
764
|
"status": "supported",
|
|
870
765
|
"example": "SELECT OCTET_LENGTH(region) AS n FROM rows WHERE region IS NOT NULL",
|
|
871
766
|
"notes": "Counts the UTF-8 encoding's bytes."
|
|
872
767
|
},
|
|
873
768
|
{
|
|
874
769
|
"id": "function.substring-from-for",
|
|
875
|
-
"feature": "E021-06",
|
|
876
770
|
"status": "supported",
|
|
877
771
|
"example": "SELECT SUBSTRING(region FROM 1 FOR 2) AS part FROM rows WHERE region IS NOT NULL",
|
|
878
772
|
"notes": "The position window is intersected with the string, so a start below 1 shortens the result instead of shifting it."
|
|
879
773
|
},
|
|
880
774
|
{
|
|
881
775
|
"id": "function.trim-specification",
|
|
882
|
-
"feature": "E021-09",
|
|
883
776
|
"status": "supported",
|
|
884
777
|
"example": "SELECT TRIM(LEADING 'w' FROM region) AS trimmed FROM rows WHERE region IS NOT NULL"
|
|
885
778
|
},
|
|
886
779
|
{
|
|
887
780
|
"id": "function.trim-multi-character",
|
|
888
|
-
"feature": "T056",
|
|
889
781
|
"status": "supported",
|
|
890
782
|
"example": "SELECT TRIM(BOTH 'we' FROM region) AS trimmed FROM rows WHERE region IS NOT NULL",
|
|
891
|
-
"notes": "
|
|
783
|
+
"notes": "Minnow removes a multi-character trim string as a repeated unit; PostgreSQL reads the argument as a set of characters instead."
|
|
892
784
|
},
|
|
893
785
|
{
|
|
894
786
|
"id": "function.position",
|
|
895
|
-
"feature": "E021-11",
|
|
896
787
|
"status": "supported",
|
|
897
788
|
"example": "SELECT POSITION('es' IN region) AS at FROM rows WHERE region IS NOT NULL"
|
|
898
789
|
},
|
|
899
790
|
{
|
|
900
791
|
"id": "function.pad",
|
|
901
|
-
"feature": "T055",
|
|
902
792
|
"status": "supported",
|
|
903
793
|
"example": "SELECT LPAD(region, 6, '-') AS padded FROM rows WHERE region IS NOT NULL"
|
|
904
794
|
},
|
|
905
795
|
{
|
|
906
796
|
"id": "function.overlay",
|
|
907
|
-
"feature": "T042",
|
|
908
797
|
"status": "supported",
|
|
909
798
|
"example": "SELECT OVERLAY(region PLACING 'X' FROM 1 FOR 1) AS masked FROM rows WHERE region IS NOT NULL"
|
|
910
799
|
},
|
|
911
800
|
{
|
|
912
801
|
"id": "select.qualified-wildcard",
|
|
913
|
-
"feature": "E051-07",
|
|
914
802
|
"status": "supported",
|
|
915
803
|
"example": "SELECT rows.* FROM rows",
|
|
916
804
|
"notes": "Output names follow the rule a bare * uses: the column's own name from one source, alias-qualified from several."
|
|
917
805
|
},
|
|
918
806
|
{
|
|
919
807
|
"id": "from.column-alias-list",
|
|
920
|
-
"feature": "E051-09",
|
|
921
808
|
"status": "supported",
|
|
922
809
|
"example": "SELECT y.a AS a FROM rows AS y(a, b, c, d)"
|
|
923
810
|
},
|
|
924
811
|
{
|
|
925
812
|
"id": "aggregate.all-quantifier",
|
|
926
|
-
"feature": "E091-06",
|
|
927
813
|
"status": "supported",
|
|
928
814
|
"example": "SELECT SUM(ALL amount) AS total FROM rows"
|
|
929
815
|
},
|
|
930
816
|
{
|
|
931
817
|
"id": "derived-table.set-operation",
|
|
932
|
-
"feature": "E071-06",
|
|
933
818
|
"status": "supported",
|
|
934
819
|
"example": "SELECT s.amount AS amount FROM (SELECT amount FROM rows UNION SELECT amount FROM dims) s"
|
|
935
820
|
},
|
|
936
821
|
{
|
|
937
822
|
"id": "comment.simple",
|
|
938
|
-
"feature": "E161",
|
|
939
823
|
"status": "supported",
|
|
940
824
|
"example": "SELECT amount FROM rows -- a comment"
|
|
941
825
|
},
|
|
942
826
|
{
|
|
943
827
|
"id": "comment.bracketed",
|
|
944
|
-
"feature": "T351",
|
|
945
828
|
"status": "supported",
|
|
946
829
|
"example": "SELECT /* a comment */ amount FROM rows"
|
|
947
830
|
},
|
|
948
831
|
{
|
|
949
832
|
"id": "join.comma",
|
|
950
|
-
"feature": "F041-07",
|
|
951
833
|
"status": "supported",
|
|
952
834
|
"example": "SELECT rows.amount AS amount FROM rows, dims WHERE dims.region = rows.region"
|
|
953
835
|
},
|
|
954
836
|
{
|
|
955
837
|
"id": "join.using",
|
|
956
|
-
"feature": "F401-04",
|
|
957
838
|
"status": "supported",
|
|
958
839
|
"example": "SELECT rows.amount AS amount FROM rows JOIN dims USING (region)",
|
|
959
|
-
"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."
|
|
960
841
|
},
|
|
961
842
|
{
|
|
962
843
|
"id": "join.natural",
|
|
963
|
-
"feature": "F401-01",
|
|
964
844
|
"status": "supported",
|
|
965
845
|
"example": "SELECT rows.amount AS amount FROM rows NATURAL JOIN dims",
|
|
966
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."
|
|
967
847
|
},
|
|
968
848
|
{
|
|
969
849
|
"id": "datetime.current-date",
|
|
970
|
-
"feature": "F051-06",
|
|
971
850
|
"status": "supported",
|
|
972
851
|
"example": "SELECT CURRENT_DATE > DATE '2000-01-01' AS elapsed",
|
|
973
852
|
"notes": "Resolved once per execution, so every row of a statement sees one instant; results that read the clock never memoize."
|
|
974
853
|
},
|
|
975
854
|
{
|
|
976
855
|
"id": "datetime.current-timestamp",
|
|
977
|
-
"feature": "F051-08",
|
|
978
856
|
"status": "supported",
|
|
979
857
|
"example": "SELECT CURRENT_TIMESTAMP > TIMESTAMP '2000-01-01 00:00:00' AS elapsed"
|
|
980
858
|
},
|
|
981
859
|
{
|
|
982
860
|
"id": "datetime.localtime",
|
|
983
|
-
"feature": "F051-07",
|
|
984
861
|
"status": "supported",
|
|
985
862
|
"example": "SELECT LOCALTIME IS NOT NULL AS ticking",
|
|
986
863
|
"notes": "The engine has no TIME type, so LOCALTIME reads as an 'HH:MM:SS' string, like SQLite's CURRENT_TIME."
|
|
987
864
|
},
|
|
988
865
|
{
|
|
989
866
|
"id": "predicate.row-comparison",
|
|
990
|
-
"feature": "F641",
|
|
991
867
|
"status": "supported",
|
|
992
868
|
"example": "SELECT amount FROM rows WHERE (region, amount) = ('west', 10)"
|
|
993
869
|
},
|
|
994
870
|
{
|
|
995
871
|
"id": "predicate.row-in",
|
|
996
|
-
"feature": "F641",
|
|
997
872
|
"status": "supported",
|
|
998
873
|
"example": "SELECT amount FROM rows WHERE (region, amount) IN (('west', 10), ('east', 3))"
|
|
999
874
|
},
|
|
1000
875
|
{
|
|
1001
876
|
"id": "predicate.row-null",
|
|
1002
|
-
"feature": "F641",
|
|
1003
877
|
"status": "supported",
|
|
1004
878
|
"example": "SELECT amount FROM rows WHERE (region, region) IS NOT NULL"
|
|
1005
879
|
},
|
|
1006
880
|
{
|
|
1007
881
|
"id": "literal.radix",
|
|
1008
|
-
"feature": "T661",
|
|
1009
882
|
"status": "supported",
|
|
1010
883
|
"example": "SELECT 0x0A AS ten"
|
|
1011
884
|
},
|
|
1012
885
|
{
|
|
1013
886
|
"id": "literal.digit-separator",
|
|
1014
|
-
"feature": "T662",
|
|
1015
887
|
"status": "supported",
|
|
1016
888
|
"example": "SELECT 1_000 AS thousand"
|
|
1017
889
|
},
|
|
1018
890
|
{
|
|
1019
891
|
"id": "limit.with-ties",
|
|
1020
|
-
"feature": "F866",
|
|
1021
892
|
"status": "supported",
|
|
1022
893
|
"example": "SELECT region FROM rows WHERE region IS NOT NULL ORDER BY region DESC FETCH FIRST 1 ROWS WITH TIES",
|
|
1023
894
|
"notes": "The limit cannot be pushed into a scan, so these plans run unlimited and the ordered result is trimmed."
|
|
1024
895
|
},
|
|
1025
896
|
{
|
|
1026
897
|
"id": "cte.in-subquery",
|
|
1027
|
-
"feature": "T122",
|
|
1028
898
|
"status": "supported",
|
|
1029
899
|
"example": "SELECT s.amount AS amount FROM (WITH inner_cte AS (SELECT amount FROM rows) SELECT amount FROM inner_cte) s"
|
|
1030
900
|
},
|
|
1031
901
|
{
|
|
1032
902
|
"id": "window.nth-value",
|
|
1033
|
-
"feature": "T618",
|
|
1034
903
|
"status": "supported",
|
|
1035
904
|
"example": "SELECT NTH_VALUE(amount, 2) OVER (ORDER BY amount) AS second FROM rows"
|
|
1036
905
|
},
|
|
1037
906
|
{
|
|
1038
907
|
"id": "window.named",
|
|
1039
|
-
"feature": "T620",
|
|
1040
908
|
"status": "supported",
|
|
1041
909
|
"example": "SELECT SUM(amount) OVER w AS running FROM rows WINDOW w AS (ORDER BY amount)"
|
|
1042
910
|
},
|
|
1043
911
|
{
|
|
1044
912
|
"id": "window.frame-groups",
|
|
1045
|
-
"feature": "T612",
|
|
1046
913
|
"status": "supported",
|
|
1047
914
|
"example": "SELECT COUNT(*) OVER (ORDER BY amount GROUPS BETWEEN 1 PRECEDING AND CURRENT ROW) AS peers FROM rows"
|
|
1048
915
|
},
|
|
1049
916
|
{
|
|
1050
917
|
"id": "window.frame-exclude",
|
|
1051
|
-
"feature": "T612",
|
|
1052
918
|
"status": "supported",
|
|
1053
919
|
"example": "SELECT COUNT(*) OVER (ORDER BY amount RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW) AS others FROM rows"
|
|
1054
920
|
},
|
|
1055
921
|
{
|
|
1056
922
|
"id": "aggregate.grouping",
|
|
1057
|
-
"feature": "T433",
|
|
1058
923
|
"status": "supported",
|
|
1059
924
|
"example": "SELECT GROUPING(region) AS aggregated FROM rows GROUP BY ROLLUP(region)",
|
|
1060
925
|
"notes": "A bitmask over the arguments, most significant first."
|
|
1061
926
|
},
|
|
1062
927
|
{
|
|
1063
928
|
"id": "aggregate.any-value",
|
|
1064
|
-
"feature": "T626",
|
|
1065
929
|
"status": "supported",
|
|
1066
930
|
"example": "SELECT ANY_VALUE(amount) AS sample FROM rows",
|
|
1067
931
|
"notes": "Which row of the group answers is implementation-dependent; this engine returns the minimum."
|
|
1068
932
|
},
|
|
1069
933
|
{
|
|
1070
934
|
"id": "aggregate.variance",
|
|
1071
|
-
"feature": "T621",
|
|
1072
935
|
"status": "supported",
|
|
1073
936
|
"example": "SELECT VAR_POP(amount) AS spread FROM rows",
|
|
1074
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."
|
|
1075
938
|
},
|
|
1076
939
|
{
|
|
1077
940
|
"id": "aggregate.stddev",
|
|
1078
|
-
"feature": "T621",
|
|
1079
941
|
"status": "supported",
|
|
1080
942
|
"example": "SELECT STDDEV_POP(amount) AS spread FROM rows"
|
|
1081
943
|
},
|
|
1082
944
|
{
|
|
1083
945
|
"id": "aggregate.boolean",
|
|
1084
|
-
"feature": "T631",
|
|
1085
946
|
"status": "supported",
|
|
1086
947
|
"example": "SELECT EVERY(amount > 1) AS all_positive FROM rows"
|
|
1087
948
|
},
|
|
1088
949
|
{
|
|
1089
950
|
"id": "json.value",
|
|
1090
|
-
"feature": "T822",
|
|
1091
951
|
"status": "supported",
|
|
1092
952
|
"example": "SELECT JSON_VALUE('{\"a\": 1}', '$.a') AS a",
|
|
1093
|
-
"notes": "JSON
|
|
953
|
+
"notes": "Accepts JSON text and stored JSON/JSONB values. Paths support $, member steps, and array subscripts."
|
|
1094
954
|
},
|
|
1095
955
|
{
|
|
1096
956
|
"id": "json.query",
|
|
1097
|
-
"feature": "T823",
|
|
1098
957
|
"status": "supported",
|
|
1099
958
|
"example": "SELECT JSON_QUERY('{\"a\": [1, 2]}', '$.a') AS a"
|
|
1100
959
|
},
|
|
1101
960
|
{
|
|
1102
961
|
"id": "json.exists",
|
|
1103
|
-
"feature": "T821",
|
|
1104
962
|
"status": "supported",
|
|
1105
963
|
"example": "SELECT JSON_EXISTS('{\"a\": 1}', '$.a') AS present"
|
|
1106
964
|
},
|
|
1107
965
|
{
|
|
1108
966
|
"id": "json.is-json",
|
|
1109
|
-
"feature": "T825",
|
|
1110
967
|
"status": "supported",
|
|
1111
968
|
"example": "SELECT '{\"a\": 1}' IS JSON OBJECT AS shaped"
|
|
1112
969
|
},
|
|
1113
970
|
{
|
|
1114
971
|
"id": "json.object",
|
|
1115
|
-
"feature": "T811",
|
|
1116
972
|
"status": "supported",
|
|
1117
|
-
"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."
|
|
1118
975
|
},
|
|
1119
976
|
{
|
|
1120
977
|
"id": "json.array",
|
|
1121
|
-
"feature": "T812",
|
|
1122
978
|
"status": "supported",
|
|
1123
|
-
"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."
|
|
1124
981
|
},
|
|
1125
982
|
{
|
|
1126
983
|
"id": "ddl.create-table-if-not-exists",
|
|
1127
|
-
"feature": "F031-01",
|
|
1128
984
|
"status": "supported",
|
|
1129
985
|
"example": "CREATE TABLE IF NOT EXISTS made (a INTEGER)"
|
|
1130
986
|
},
|
|
1131
987
|
{
|
|
1132
988
|
"id": "ddl.create-table-default",
|
|
1133
|
-
"feature": "E141-07",
|
|
1134
989
|
"status": "supported",
|
|
1135
990
|
"example": "CREATE TABLE defaulted (id INTEGER PRIMARY KEY, tier TEXT DEFAULT 'basic')",
|
|
1136
|
-
"notes": "
|
|
991
|
+
"notes": "DEFAULT accepts a variable-free scalar expression. Omission or SQL DEFAULT invokes it; explicit NULL follows the column's independent nullability."
|
|
1137
992
|
},
|
|
1138
993
|
{
|
|
1139
994
|
"id": "ddl.create-table-key-clause",
|
|
1140
|
-
"feature": "E141-08",
|
|
1141
995
|
"status": "supported",
|
|
1142
996
|
"example": "CREATE TABLE keyed_clause (a INTEGER, b TEXT, PRIMARY KEY (a))"
|
|
1143
997
|
},
|
|
1144
998
|
{
|
|
1145
999
|
"id": "ddl.alter-table-add-column",
|
|
1146
|
-
"feature": "F031-04",
|
|
1147
1000
|
"status": "supported",
|
|
1148
1001
|
"example": "ALTER TABLE rows ADD COLUMN note TEXT",
|
|
1149
1002
|
"notes": "Existing rows have no value for the new column, so it is always nullable."
|
|
1150
1003
|
},
|
|
1151
1004
|
{
|
|
1152
1005
|
"id": "ddl.create-table-as-select",
|
|
1153
|
-
"feature": "T172",
|
|
1154
1006
|
"status": "supported",
|
|
1155
1007
|
"example": "CREATE TABLE copied AS SELECT region FROM rows"
|
|
1156
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
|
+
},
|
|
1157
1109
|
{
|
|
1158
1110
|
"id": "privileges.grant",
|
|
1159
|
-
"feature": "E081",
|
|
1160
1111
|
"status": "unsupported",
|
|
1161
1112
|
"example": "GRANT SELECT ON rows TO reader",
|
|
1162
1113
|
"error": "Expected SELECT, found GRANT",
|
|
@@ -1164,79 +1115,60 @@
|
|
|
1164
1115
|
},
|
|
1165
1116
|
{
|
|
1166
1117
|
"id": "from.lateral",
|
|
1167
|
-
"
|
|
1168
|
-
"
|
|
1169
|
-
"
|
|
1170
|
-
"error": "LATERAL sources are not supported",
|
|
1171
|
-
"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."
|
|
1172
1121
|
},
|
|
1173
1122
|
{
|
|
1174
|
-
"id": "aggregate.
|
|
1175
|
-
"
|
|
1176
|
-
"
|
|
1177
|
-
"
|
|
1178
|
-
"error": "Unsupported function: LISTAGG",
|
|
1179
|
-
"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."
|
|
1180
1127
|
},
|
|
1181
1128
|
{
|
|
1182
1129
|
"id": "json.table",
|
|
1183
|
-
"
|
|
1184
|
-
"
|
|
1185
|
-
"
|
|
1186
|
-
"error": "JSON_TABLE is not supported",
|
|
1187
|
-
"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."
|
|
1188
1133
|
},
|
|
1189
1134
|
{
|
|
1190
1135
|
"id": "predicate.similar-to",
|
|
1191
|
-
"
|
|
1192
|
-
"status": "unsupported",
|
|
1136
|
+
"status": "supported",
|
|
1193
1137
|
"example": "SELECT amount FROM rows WHERE region SIMILAR TO 'w%'",
|
|
1194
|
-
"
|
|
1195
|
-
"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."
|
|
1196
1139
|
},
|
|
1197
1140
|
{
|
|
1198
1141
|
"id": "collation.explicit",
|
|
1199
|
-
"
|
|
1200
|
-
"
|
|
1201
|
-
"
|
|
1202
|
-
"error": "Expected eof, found COLLATE",
|
|
1203
|
-
"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."
|
|
1204
1145
|
},
|
|
1205
1146
|
{
|
|
1206
1147
|
"id": "aggregate.json",
|
|
1207
|
-
"
|
|
1208
|
-
"status": "unsupported",
|
|
1148
|
+
"status": "supported",
|
|
1209
1149
|
"example": "SELECT JSON_ARRAYAGG(region) AS regions FROM rows",
|
|
1210
|
-
"
|
|
1211
|
-
"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."
|
|
1212
1151
|
},
|
|
1213
1152
|
{
|
|
1214
1153
|
"id": "type.array",
|
|
1215
|
-
"
|
|
1216
|
-
"status": "unsupported",
|
|
1154
|
+
"status": "supported",
|
|
1217
1155
|
"example": "SELECT ARRAY[1, 2] AS pair",
|
|
1218
|
-
"
|
|
1219
|
-
"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."
|
|
1220
1157
|
},
|
|
1221
1158
|
{
|
|
1222
1159
|
"id": "type.time",
|
|
1223
|
-
"
|
|
1224
|
-
"status": "unsupported",
|
|
1160
|
+
"status": "supported",
|
|
1225
1161
|
"example": "SELECT TIME '12:00:00' AS at",
|
|
1226
|
-
"
|
|
1227
|
-
"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."
|
|
1228
1163
|
},
|
|
1229
1164
|
{
|
|
1230
1165
|
"id": "ddl.sequence",
|
|
1231
|
-
"
|
|
1232
|
-
"status": "unsupported",
|
|
1166
|
+
"status": "supported",
|
|
1233
1167
|
"example": "CREATE SEQUENCE order_ids",
|
|
1234
|
-
"
|
|
1235
|
-
"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."
|
|
1236
1169
|
},
|
|
1237
1170
|
{
|
|
1238
1171
|
"id": "ddl.drop-table",
|
|
1239
|
-
"feature": "F031-13",
|
|
1240
1172
|
"status": "supported",
|
|
1241
1173
|
"setup": ["CREATE TABLE doomed (a INTEGER)"],
|
|
1242
1174
|
"example": "DROP TABLE doomed",
|
|
@@ -1244,32 +1176,28 @@
|
|
|
1244
1176
|
},
|
|
1245
1177
|
{
|
|
1246
1178
|
"id": "ddl.create-view",
|
|
1247
|
-
"feature": "F031-02",
|
|
1248
1179
|
"status": "supported",
|
|
1249
1180
|
"example": "CREATE VIEW west AS SELECT region, amount FROM rows WHERE region = 'west'",
|
|
1250
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."
|
|
1251
1182
|
},
|
|
1252
1183
|
{
|
|
1253
1184
|
"id": "ddl.drop-view",
|
|
1254
|
-
"feature": "F031-16",
|
|
1255
1185
|
"status": "supported",
|
|
1256
1186
|
"setup": ["CREATE VIEW doomed_view AS SELECT amount FROM rows"],
|
|
1257
1187
|
"example": "DROP VIEW doomed_view"
|
|
1258
1188
|
},
|
|
1259
1189
|
{
|
|
1260
1190
|
"id": "ddl.check-constraint",
|
|
1261
|
-
"feature": "E141-06",
|
|
1262
1191
|
"status": "supported",
|
|
1263
1192
|
"example": "CREATE TABLE checked (a INTEGER NOT NULL CHECK (a > 0), CONSTRAINT small CHECK (a < 100))",
|
|
1264
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."
|
|
1265
1194
|
},
|
|
1266
1195
|
{
|
|
1267
1196
|
"id": "ddl.foreign-key",
|
|
1268
|
-
"feature": "E141-04",
|
|
1269
1197
|
"status": "supported",
|
|
1270
1198
|
"setup": ["CREATE TABLE parents (id INTEGER PRIMARY KEY, label TEXT NOT NULL)"],
|
|
1271
1199
|
"example": "CREATE TABLE children (id INTEGER PRIMARY KEY, parent INTEGER REFERENCES parents(id) ON DELETE CASCADE)",
|
|
1272
|
-
"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."
|
|
1273
1201
|
}
|
|
1274
1202
|
]
|
|
1275
1203
|
}
|