@jarenjs/db 0.73.0 → 0.83.2
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/ARCHITECTURE.md +70 -7
- package/README.md +69 -6
- package/docs/HOSTS.md +17 -0
- package/docs/JOBS-FORMAT.md +26 -0
- package/docs/LIVE-FORMAT.md +52 -13
- package/docs/MIGRATION-FORMAT.md +34 -0
- package/docs/MODEL-FORMAT.md +163 -15
- package/docs/NATIVE-PLANS.md +111 -0
- package/docs/REPLICATION-FORMAT.md +19 -13
- package/docs/SEARCH.md +55 -0
- package/package.json +8 -4
- package/schemas/jaren-migration.draft-07.schema.json +54 -5
- package/schemas/jaren-migration.schema.json +49 -0
- package/schemas/jaren-model.authoring.schema.json +360 -0
- package/schemas/jaren-model.draft-07.schema.json +128 -0
- package/schemas/jaren-model.schema.json +128 -0
- package/src/algebra.js +26 -4
- package/src/backup.js +12 -7
- package/src/cursor.js +27 -4
- package/src/dag-job.js +2 -1
- package/src/ddl.js +13 -0
- package/src/derive.js +14 -3
- package/src/dialect.js +12 -0
- package/src/dialects/check-read.js +151 -0
- package/src/dialects/invariant-sql.js +117 -0
- package/src/dialects/postgres.js +28 -4
- package/src/dialects/sqlite.js +23 -3
- package/src/driver.js +1 -0
- package/src/drivers/bun.js +22 -4
- package/src/emit.js +133 -25
- package/src/entity.js +98 -41
- package/src/errors.js +8 -0
- package/src/graph.js +8 -1
- package/src/index.js +3 -0
- package/src/introspect.js +81 -12
- package/src/invariants.js +45 -0
- package/src/jobs.js +39 -6
- package/src/live-nested.js +27 -10
- package/src/live.js +51 -136
- package/src/migrate.js +136 -22
- package/src/model.js +12 -0
- package/src/mutation.js +165 -0
- package/src/physical.js +147 -0
- package/src/plan.js +275 -64
- package/src/query.js +175 -78
- package/src/search.js +144 -0
- package/src/sql.js +60 -0
- package/src/store.js +49 -13
- package/src/tracker.js +63 -39
- package/src/window.js +1 -0
- package/types/index.d.ts +59 -4
- package/types/search.d.ts +20 -0
- package/types/typed.d.ts +1 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://jarenjs.dev/schemas/jaren-model/authoring",
|
|
4
|
+
"title": "Jaren model document (authoring profile)",
|
|
5
|
+
"description": "Authoring profile of the canonical grammar: the document SHAPE only, with left open. Constrains decoding on small models, where the full grammar does not decode at all. It is deliberately WEAKER than the canonical schema - a document valid here may be nonsense - so the engine compiler is the gate that makes it safe, and generated documents must be compiled before use.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"$model": {
|
|
9
|
+
"description": "The model format version.",
|
|
10
|
+
"const": "0.1"
|
|
11
|
+
},
|
|
12
|
+
"collections": {
|
|
13
|
+
"description": "Collection name (an identifier) to collection declaration. (LLM profile: a member-name constraint is relaxed here; the canonical schema enforces it)",
|
|
14
|
+
"type": "object",
|
|
15
|
+
"minProperties": 1,
|
|
16
|
+
"additionalProperties": {
|
|
17
|
+
"$ref": "#/$defs/collection"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"entities": {
|
|
21
|
+
"description": "Entity name (an identifier) to entity declaration. The x-entity vocabulary lives INSIDE each entity schema's property subschemas and is documented in MODEL-FORMAT.md §9; stripping it leaves a plain JSON Schema. (LLM profile: a member-name constraint is relaxed here; the canonical schema enforces it)",
|
|
22
|
+
"type": "object",
|
|
23
|
+
"minProperties": 1,
|
|
24
|
+
"additionalProperties": {
|
|
25
|
+
"$ref": "#/$defs/entity"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"required": [
|
|
30
|
+
"$model"
|
|
31
|
+
],
|
|
32
|
+
"additionalProperties": false,
|
|
33
|
+
"anyOf": [
|
|
34
|
+
{
|
|
35
|
+
"required": [
|
|
36
|
+
"collections"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"required": [
|
|
41
|
+
"entities"
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"$defs": {
|
|
46
|
+
"collection": {
|
|
47
|
+
"description": "One collection: a document schema, the key declaration, and the declared indexes. A collection without a key pointer MUST declare how keys are allocated; allocation is declared, never guessed.",
|
|
48
|
+
"type": "object",
|
|
49
|
+
"properties": {
|
|
50
|
+
"schema": {
|
|
51
|
+
"description": "The JSON Schema every stored document validates against (through the injected hook). Also the type source for indexed paths.",
|
|
52
|
+
"type": "object"
|
|
53
|
+
},
|
|
54
|
+
"key": {
|
|
55
|
+
"description": "Where the caller-supplied key lives in the document, as an RFC 6901 pointer selecting at least one member — or null when the store allocates.",
|
|
56
|
+
"type": [
|
|
57
|
+
"string",
|
|
58
|
+
"null"
|
|
59
|
+
],
|
|
60
|
+
"pattern": "^/"
|
|
61
|
+
},
|
|
62
|
+
"identity": {
|
|
63
|
+
"description": "How keys come to be: 'caller' (the default when a key pointer is declared), or — only with \"key\": null — 'uuid' (crypto.randomUUID) or 'integer' (database-allocated).",
|
|
64
|
+
"enum": [
|
|
65
|
+
"caller",
|
|
66
|
+
"uuid",
|
|
67
|
+
"integer"
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
"indexes": {
|
|
71
|
+
"description": "Declared indexes; each singular path becomes a generated column plus an index.",
|
|
72
|
+
"type": "array",
|
|
73
|
+
"items": {
|
|
74
|
+
"$ref": "#/$defs/index"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"x-rename": {
|
|
78
|
+
"description": "Migration hint: this collection was previously named the given identifier. Read by the migration planner (a rename cannot be inferred from a diff); ignored by the store.",
|
|
79
|
+
"type": "string",
|
|
80
|
+
"pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"required": [
|
|
84
|
+
"schema"
|
|
85
|
+
],
|
|
86
|
+
"additionalProperties": false
|
|
87
|
+
},
|
|
88
|
+
"index": {
|
|
89
|
+
"description": "One index over a singular JSONPath expression (or a composite over several). A non-singular path is rejected at open (JD0004). A derived index computes indexable scalars from the selected member instead of indexing it directly. An index may instead declare an expression: a closed computation over members, scalars and host-declared deterministic functions.",
|
|
90
|
+
"type": "object",
|
|
91
|
+
"properties": {
|
|
92
|
+
"name": {
|
|
93
|
+
"description": "The index name, unique within its collection.",
|
|
94
|
+
"type": "string",
|
|
95
|
+
"pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
|
|
96
|
+
},
|
|
97
|
+
"path": {
|
|
98
|
+
"description": "A singular JSONPath expression into the stored document ($.email), or a non-empty array of them for a composite index.",
|
|
99
|
+
"anyOf": [
|
|
100
|
+
{
|
|
101
|
+
"type": "string",
|
|
102
|
+
"minLength": 1
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"type": "array",
|
|
106
|
+
"items": {
|
|
107
|
+
"type": "string",
|
|
108
|
+
"minLength": 1
|
|
109
|
+
},
|
|
110
|
+
"minItems": 1
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
"expression": {
|
|
115
|
+
"description": "Index a computed value rather than a member: a closed expression over declared members, JSON scalars and functions the host declared deterministic. Mutually exclusive with path and derive (JD0004) — an expression names the members it reads itself.",
|
|
116
|
+
"allOf": [
|
|
117
|
+
{
|
|
118
|
+
"$ref": "#/$defs/expression"
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
"unique": {
|
|
123
|
+
"description": "Whether the index enforces uniqueness. Defaults to false.",
|
|
124
|
+
"type": "boolean"
|
|
125
|
+
},
|
|
126
|
+
"derive": {
|
|
127
|
+
"description": "Derive columns from the selected member instead of indexing it: 'geohash' (one TEXT cell column, precision required), 'bbox' (four REAL columns — west, south, east, north), or 'vector' (one packed BLOB column holding the l2-normalized Float32 form of an array of numbers, dims required; stored on every driver, with no B-tree over it — a fetch-and-rank plan reads it whole). A derived index is never unique.",
|
|
128
|
+
"enum": [
|
|
129
|
+
"geohash",
|
|
130
|
+
"bbox",
|
|
131
|
+
"vector"
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
"precision": {
|
|
135
|
+
"description": "Geohash cell length in characters, 1-12. Required beside derive: 'geohash' and refused anywhere else — there is no safe default, because the right cell size depends on the query radius, which the model cannot know.",
|
|
136
|
+
"type": "integer",
|
|
137
|
+
"minimum": 1,
|
|
138
|
+
"maximum": 12
|
|
139
|
+
},
|
|
140
|
+
"dims": {
|
|
141
|
+
"description": "The width of a derive: 'vector' column in components, 1-8192. Required beside derive: 'vector' and refused anywhere else: the width is the column's identity — two widths over one path are two columns — and a member that is not an array of exactly this many finite numbers stores NULL in the column (the document itself still stores). Constrain the member with minItems/maxItems equal to dims in the collection's schema so a wrong-width vector is refused at the write instead.",
|
|
142
|
+
"type": "integer",
|
|
143
|
+
"minimum": 1,
|
|
144
|
+
"maximum": 8192
|
|
145
|
+
},
|
|
146
|
+
"physical": {
|
|
147
|
+
"description": "The shape a derive: 'bbox' index takes on disk: 'columns' (the default) is four generated columns under one B-tree; 'rtree' keeps the same four columns and adds a SQLite R*Tree virtual table beside the collection, kept in sync by declared triggers, with no B-tree over the columns. The logical meaning of derive: 'bbox' is identical either way. Refused on any other index kind, and on a driver without the module it falls back to 'columns' and says so through explain().prefilters[].via.",
|
|
148
|
+
"enum": [
|
|
149
|
+
"columns",
|
|
150
|
+
"rtree"
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
"required": [
|
|
155
|
+
"name"
|
|
156
|
+
],
|
|
157
|
+
"additionalProperties": false
|
|
158
|
+
},
|
|
159
|
+
"entity": {
|
|
160
|
+
"description": "One entity: an object schema whose top-level properties map to columns per the hybrid rule (scalars become typed columns, nested shapes stay JSONB); keys, indexes, defaults and relations are declared with x-entity inside the schema.",
|
|
161
|
+
"type": "object",
|
|
162
|
+
"properties": {
|
|
163
|
+
"schema": {
|
|
164
|
+
"type": "object"
|
|
165
|
+
},
|
|
166
|
+
"x-rename": {
|
|
167
|
+
"type": "string",
|
|
168
|
+
"description": "Declares this entity as the rename of the named from-model entity (MIGRATION-FORMAT §9): a rename is declared, never inferred."
|
|
169
|
+
},
|
|
170
|
+
"physical": {
|
|
171
|
+
"type": "object",
|
|
172
|
+
"required": [
|
|
173
|
+
"table",
|
|
174
|
+
"columns"
|
|
175
|
+
],
|
|
176
|
+
"properties": {
|
|
177
|
+
"table": {
|
|
178
|
+
"type": "string",
|
|
179
|
+
"minLength": 1
|
|
180
|
+
},
|
|
181
|
+
"kind": {
|
|
182
|
+
"enum": [
|
|
183
|
+
"table",
|
|
184
|
+
"view"
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
"keys": {
|
|
188
|
+
"type": "array",
|
|
189
|
+
"items": {
|
|
190
|
+
"type": "string"
|
|
191
|
+
},
|
|
192
|
+
"minItems": 1,
|
|
193
|
+
"uniqueItems": true
|
|
194
|
+
},
|
|
195
|
+
"columns": {
|
|
196
|
+
"type": "object",
|
|
197
|
+
"additionalProperties": {
|
|
198
|
+
"type": "object",
|
|
199
|
+
"required": [
|
|
200
|
+
"name",
|
|
201
|
+
"codec",
|
|
202
|
+
"null"
|
|
203
|
+
],
|
|
204
|
+
"properties": {
|
|
205
|
+
"name": {
|
|
206
|
+
"type": "string",
|
|
207
|
+
"minLength": 1
|
|
208
|
+
},
|
|
209
|
+
"codec": {
|
|
210
|
+
"enum": [
|
|
211
|
+
"text",
|
|
212
|
+
"integer",
|
|
213
|
+
"number",
|
|
214
|
+
"boolean",
|
|
215
|
+
"json",
|
|
216
|
+
"date",
|
|
217
|
+
"datetime",
|
|
218
|
+
"epoch-ms",
|
|
219
|
+
"bigint",
|
|
220
|
+
"decimal",
|
|
221
|
+
"blob-hex"
|
|
222
|
+
]
|
|
223
|
+
},
|
|
224
|
+
"null": {
|
|
225
|
+
"enum": [
|
|
226
|
+
"null",
|
|
227
|
+
"absent",
|
|
228
|
+
"reject"
|
|
229
|
+
]
|
|
230
|
+
},
|
|
231
|
+
"default": {
|
|
232
|
+
"const": "database"
|
|
233
|
+
},
|
|
234
|
+
"generated": {
|
|
235
|
+
"type": "boolean"
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
"additionalProperties": false
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
"additionalProperties": false
|
|
243
|
+
},
|
|
244
|
+
"invariants": {
|
|
245
|
+
"type": "array",
|
|
246
|
+
"items": {
|
|
247
|
+
"type": "object",
|
|
248
|
+
"required": [
|
|
249
|
+
"name",
|
|
250
|
+
"on",
|
|
251
|
+
"assert",
|
|
252
|
+
"enforcement"
|
|
253
|
+
],
|
|
254
|
+
"properties": {
|
|
255
|
+
"name": {
|
|
256
|
+
"type": "string",
|
|
257
|
+
"pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
|
|
258
|
+
},
|
|
259
|
+
"on": {
|
|
260
|
+
"type": "array",
|
|
261
|
+
"minItems": 1,
|
|
262
|
+
"uniqueItems": true,
|
|
263
|
+
"items": {
|
|
264
|
+
"enum": [
|
|
265
|
+
"insert",
|
|
266
|
+
"update",
|
|
267
|
+
"delete"
|
|
268
|
+
]
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
"assert": {},
|
|
272
|
+
"enforcement": {
|
|
273
|
+
"enum": [
|
|
274
|
+
"database",
|
|
275
|
+
"store"
|
|
276
|
+
]
|
|
277
|
+
},
|
|
278
|
+
"audit": {
|
|
279
|
+
"type": "object",
|
|
280
|
+
"required": [
|
|
281
|
+
"entity",
|
|
282
|
+
"values"
|
|
283
|
+
],
|
|
284
|
+
"properties": {
|
|
285
|
+
"entity": {
|
|
286
|
+
"type": "string"
|
|
287
|
+
},
|
|
288
|
+
"values": {
|
|
289
|
+
"type": "object"
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
"additionalProperties": false
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
"additionalProperties": false
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
"required": [
|
|
300
|
+
"schema"
|
|
301
|
+
],
|
|
302
|
+
"additionalProperties": false
|
|
303
|
+
},
|
|
304
|
+
"expression": {
|
|
305
|
+
"description": "One node of an index expression: a member path, a JSON scalar, or a call to a function the host declared. Exactly one of the three, and nothing else — an index expression is never SQL text, and the function it names is resolved for arity and determinism at open (JD0004).",
|
|
306
|
+
"type": "object",
|
|
307
|
+
"anyOf": [
|
|
308
|
+
{
|
|
309
|
+
"required": [
|
|
310
|
+
"member"
|
|
311
|
+
],
|
|
312
|
+
"properties": {
|
|
313
|
+
"member": {
|
|
314
|
+
"description": "A singular JSONPath expression into the stored document ($.email).",
|
|
315
|
+
"type": "string",
|
|
316
|
+
"minLength": 1
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
"additionalProperties": false
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"required": [
|
|
323
|
+
"value"
|
|
324
|
+
],
|
|
325
|
+
"properties": {
|
|
326
|
+
"value": {
|
|
327
|
+
"description": "A JSON scalar. A null or a compound has no place in an index expression.",
|
|
328
|
+
"type": [
|
|
329
|
+
"string",
|
|
330
|
+
"number",
|
|
331
|
+
"boolean"
|
|
332
|
+
]
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
"additionalProperties": false
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
"required": [
|
|
339
|
+
"call"
|
|
340
|
+
],
|
|
341
|
+
"properties": {
|
|
342
|
+
"call": {
|
|
343
|
+
"description": "The name of a function the host declared through openStore({ expressions }). The store refuses a name it was not given, a wrong arity, and one not declared deterministic — before any DDL.",
|
|
344
|
+
"type": "string",
|
|
345
|
+
"pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
|
|
346
|
+
},
|
|
347
|
+
"args": {
|
|
348
|
+
"description": "The arguments, in order. Order is significant: sub(a, b) and sub(b, a) are different expressions and different columns.",
|
|
349
|
+
"type": "array",
|
|
350
|
+
"items": {
|
|
351
|
+
"$ref": "#/$defs/expression"
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
"additionalProperties": false
|
|
356
|
+
}
|
|
357
|
+
]
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
@@ -160,6 +160,134 @@
|
|
|
160
160
|
"x-rename": {
|
|
161
161
|
"type": "string",
|
|
162
162
|
"description": "Declares this entity as the rename of the named from-model entity (MIGRATION-FORMAT \u00a79): a rename is declared, never inferred."
|
|
163
|
+
},
|
|
164
|
+
"physical": {
|
|
165
|
+
"type": "object",
|
|
166
|
+
"required": [
|
|
167
|
+
"table",
|
|
168
|
+
"columns"
|
|
169
|
+
],
|
|
170
|
+
"properties": {
|
|
171
|
+
"table": {
|
|
172
|
+
"type": "string",
|
|
173
|
+
"minLength": 1
|
|
174
|
+
},
|
|
175
|
+
"kind": {
|
|
176
|
+
"enum": [
|
|
177
|
+
"table",
|
|
178
|
+
"view"
|
|
179
|
+
]
|
|
180
|
+
},
|
|
181
|
+
"keys": {
|
|
182
|
+
"type": "array",
|
|
183
|
+
"items": {
|
|
184
|
+
"type": "string"
|
|
185
|
+
},
|
|
186
|
+
"minItems": 1,
|
|
187
|
+
"uniqueItems": true
|
|
188
|
+
},
|
|
189
|
+
"columns": {
|
|
190
|
+
"type": "object",
|
|
191
|
+
"additionalProperties": {
|
|
192
|
+
"type": "object",
|
|
193
|
+
"required": [
|
|
194
|
+
"name",
|
|
195
|
+
"codec",
|
|
196
|
+
"null"
|
|
197
|
+
],
|
|
198
|
+
"properties": {
|
|
199
|
+
"name": {
|
|
200
|
+
"type": "string",
|
|
201
|
+
"minLength": 1
|
|
202
|
+
},
|
|
203
|
+
"codec": {
|
|
204
|
+
"enum": [
|
|
205
|
+
"text",
|
|
206
|
+
"integer",
|
|
207
|
+
"number",
|
|
208
|
+
"boolean",
|
|
209
|
+
"json",
|
|
210
|
+
"date",
|
|
211
|
+
"datetime",
|
|
212
|
+
"epoch-ms",
|
|
213
|
+
"bigint",
|
|
214
|
+
"decimal",
|
|
215
|
+
"blob-hex"
|
|
216
|
+
]
|
|
217
|
+
},
|
|
218
|
+
"null": {
|
|
219
|
+
"enum": [
|
|
220
|
+
"null",
|
|
221
|
+
"absent",
|
|
222
|
+
"reject"
|
|
223
|
+
]
|
|
224
|
+
},
|
|
225
|
+
"default": {
|
|
226
|
+
"const": "database"
|
|
227
|
+
},
|
|
228
|
+
"generated": {
|
|
229
|
+
"type": "boolean"
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
"additionalProperties": false
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
"additionalProperties": false
|
|
237
|
+
},
|
|
238
|
+
"invariants": {
|
|
239
|
+
"type": "array",
|
|
240
|
+
"items": {
|
|
241
|
+
"type": "object",
|
|
242
|
+
"required": [
|
|
243
|
+
"name",
|
|
244
|
+
"on",
|
|
245
|
+
"assert",
|
|
246
|
+
"enforcement"
|
|
247
|
+
],
|
|
248
|
+
"properties": {
|
|
249
|
+
"name": {
|
|
250
|
+
"type": "string",
|
|
251
|
+
"pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
|
|
252
|
+
},
|
|
253
|
+
"on": {
|
|
254
|
+
"type": "array",
|
|
255
|
+
"minItems": 1,
|
|
256
|
+
"uniqueItems": true,
|
|
257
|
+
"items": {
|
|
258
|
+
"enum": [
|
|
259
|
+
"insert",
|
|
260
|
+
"update",
|
|
261
|
+
"delete"
|
|
262
|
+
]
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
"assert": {},
|
|
266
|
+
"enforcement": {
|
|
267
|
+
"enum": [
|
|
268
|
+
"database",
|
|
269
|
+
"store"
|
|
270
|
+
]
|
|
271
|
+
},
|
|
272
|
+
"audit": {
|
|
273
|
+
"type": "object",
|
|
274
|
+
"required": [
|
|
275
|
+
"entity",
|
|
276
|
+
"values"
|
|
277
|
+
],
|
|
278
|
+
"properties": {
|
|
279
|
+
"entity": {
|
|
280
|
+
"type": "string"
|
|
281
|
+
},
|
|
282
|
+
"values": {
|
|
283
|
+
"type": "object"
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
"additionalProperties": false
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
"additionalProperties": false
|
|
290
|
+
}
|
|
163
291
|
}
|
|
164
292
|
},
|
|
165
293
|
"required": [
|
|
@@ -160,6 +160,134 @@
|
|
|
160
160
|
"x-rename": {
|
|
161
161
|
"type": "string",
|
|
162
162
|
"description": "Declares this entity as the rename of the named from-model entity (MIGRATION-FORMAT \u00a79): a rename is declared, never inferred."
|
|
163
|
+
},
|
|
164
|
+
"physical": {
|
|
165
|
+
"type": "object",
|
|
166
|
+
"required": [
|
|
167
|
+
"table",
|
|
168
|
+
"columns"
|
|
169
|
+
],
|
|
170
|
+
"properties": {
|
|
171
|
+
"table": {
|
|
172
|
+
"type": "string",
|
|
173
|
+
"minLength": 1
|
|
174
|
+
},
|
|
175
|
+
"kind": {
|
|
176
|
+
"enum": [
|
|
177
|
+
"table",
|
|
178
|
+
"view"
|
|
179
|
+
]
|
|
180
|
+
},
|
|
181
|
+
"keys": {
|
|
182
|
+
"type": "array",
|
|
183
|
+
"items": {
|
|
184
|
+
"type": "string"
|
|
185
|
+
},
|
|
186
|
+
"minItems": 1,
|
|
187
|
+
"uniqueItems": true
|
|
188
|
+
},
|
|
189
|
+
"columns": {
|
|
190
|
+
"type": "object",
|
|
191
|
+
"additionalProperties": {
|
|
192
|
+
"type": "object",
|
|
193
|
+
"required": [
|
|
194
|
+
"name",
|
|
195
|
+
"codec",
|
|
196
|
+
"null"
|
|
197
|
+
],
|
|
198
|
+
"properties": {
|
|
199
|
+
"name": {
|
|
200
|
+
"type": "string",
|
|
201
|
+
"minLength": 1
|
|
202
|
+
},
|
|
203
|
+
"codec": {
|
|
204
|
+
"enum": [
|
|
205
|
+
"text",
|
|
206
|
+
"integer",
|
|
207
|
+
"number",
|
|
208
|
+
"boolean",
|
|
209
|
+
"json",
|
|
210
|
+
"date",
|
|
211
|
+
"datetime",
|
|
212
|
+
"epoch-ms",
|
|
213
|
+
"bigint",
|
|
214
|
+
"decimal",
|
|
215
|
+
"blob-hex"
|
|
216
|
+
]
|
|
217
|
+
},
|
|
218
|
+
"null": {
|
|
219
|
+
"enum": [
|
|
220
|
+
"null",
|
|
221
|
+
"absent",
|
|
222
|
+
"reject"
|
|
223
|
+
]
|
|
224
|
+
},
|
|
225
|
+
"default": {
|
|
226
|
+
"const": "database"
|
|
227
|
+
},
|
|
228
|
+
"generated": {
|
|
229
|
+
"type": "boolean"
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
"additionalProperties": false
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
"additionalProperties": false
|
|
237
|
+
},
|
|
238
|
+
"invariants": {
|
|
239
|
+
"type": "array",
|
|
240
|
+
"items": {
|
|
241
|
+
"type": "object",
|
|
242
|
+
"required": [
|
|
243
|
+
"name",
|
|
244
|
+
"on",
|
|
245
|
+
"assert",
|
|
246
|
+
"enforcement"
|
|
247
|
+
],
|
|
248
|
+
"properties": {
|
|
249
|
+
"name": {
|
|
250
|
+
"type": "string",
|
|
251
|
+
"pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
|
|
252
|
+
},
|
|
253
|
+
"on": {
|
|
254
|
+
"type": "array",
|
|
255
|
+
"minItems": 1,
|
|
256
|
+
"uniqueItems": true,
|
|
257
|
+
"items": {
|
|
258
|
+
"enum": [
|
|
259
|
+
"insert",
|
|
260
|
+
"update",
|
|
261
|
+
"delete"
|
|
262
|
+
]
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
"assert": {},
|
|
266
|
+
"enforcement": {
|
|
267
|
+
"enum": [
|
|
268
|
+
"database",
|
|
269
|
+
"store"
|
|
270
|
+
]
|
|
271
|
+
},
|
|
272
|
+
"audit": {
|
|
273
|
+
"type": "object",
|
|
274
|
+
"required": [
|
|
275
|
+
"entity",
|
|
276
|
+
"values"
|
|
277
|
+
],
|
|
278
|
+
"properties": {
|
|
279
|
+
"entity": {
|
|
280
|
+
"type": "string"
|
|
281
|
+
},
|
|
282
|
+
"values": {
|
|
283
|
+
"type": "object"
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
"additionalProperties": false
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
"additionalProperties": false
|
|
290
|
+
}
|
|
163
291
|
}
|
|
164
292
|
},
|
|
165
293
|
"required": [
|
package/src/algebra.js
CHANGED
|
@@ -50,13 +50,17 @@ export const PLAN_VERSION = 2;
|
|
|
50
50
|
* { p: 'not', item: PlanPredicate } |
|
|
51
51
|
* { p: 'cmp', op: 'eq' | 'ne' | 'lt' | 'le' | 'gt' | 'ge',
|
|
52
52
|
* ref: PlanRef, operand: PlanOperand } |
|
|
53
|
+
* { p: 'refCmp', op: 'eq' | 'ne' | 'lt' | 'le' | 'gt' | 'ge',
|
|
54
|
+
* left: PlanRef, right: PlanRef } |
|
|
53
55
|
* { p: 'typeIs', ref: PlanRef, types: string[], positive: boolean } |
|
|
54
56
|
* { p: 'strop', kind: 'starts' | 'ends' | 'contains',
|
|
55
57
|
* ref: PlanRef, operand: PlanOperand } |
|
|
56
58
|
* { p: 'const', value: boolean } |
|
|
57
59
|
* { p: 'udf', name: string, key: string } |
|
|
58
60
|
* { p: 'bboxOverlap', columns: { w: string, s: string, e: string,
|
|
59
|
-
* n: string }, probe: { box: number[] } | { ext: string }
|
|
61
|
+
* n: string }, probe: { box: number[] } | { ext: string } |
|
|
62
|
+
* { circle: { centre: { external: string } | { literal: unknown },
|
|
63
|
+
* radius: { external: string } | { literal: unknown } } } } |
|
|
60
64
|
* { p: 'cellIn', column: string, cells: string[], keepEmpty?: boolean } |
|
|
61
65
|
* { p: 'cellPrefix', column: string, prefix: string } |
|
|
62
66
|
* { p: 'interval', columns: { start: string, end: string },
|
|
@@ -123,7 +127,8 @@ export const PLAN_VERSION = 2;
|
|
|
123
127
|
* aggregates: { as: string, fn: 'rows' | 'sum' | 'avg' | 'min' | 'max',
|
|
124
128
|
* ref: PlanRef | null, empty: 'zero' | 'omit' | 'null' }[],
|
|
125
129
|
* tree: PlanProjectionNode,
|
|
126
|
-
* order: 'first-seen' | { index
|
|
130
|
+
* order: 'first-seen' | { index?: number, aggregate?: number,
|
|
131
|
+
* desc: boolean, nullsFirst: boolean }[]
|
|
127
132
|
* }} PlanGroup
|
|
128
133
|
* The GENERAL `GROUP BY`: one key per declared grouping name, the
|
|
129
134
|
* closed aggregate set over the group's rows, and the projection tree
|
|
@@ -133,7 +138,8 @@ export const PLAN_VERSION = 2;
|
|
|
133
138
|
* an `agg` node reads one aggregate under its `empty` rule. `order` is
|
|
134
139
|
* `'first-seen'`, the engine's own order of first appearance, or the
|
|
135
140
|
* group-key ordering an `$orderby` declared. A plan carrying a group
|
|
136
|
-
* carries no `bucket
|
|
141
|
+
* carries no `bucket` or `rank`. A `count` aggregate may wrap a
|
|
142
|
+
* singleton group constructor, counting groups through a subquery.
|
|
137
143
|
*
|
|
138
144
|
* @typedef {{ ref: PlanRef, every: number, origin: number, as: string,
|
|
139
145
|
* order: 'asc' | 'desc' | 'first-seen',
|
|
@@ -329,7 +335,23 @@ export function planOrder(plan) {
|
|
|
329
335
|
if (plan === null || plan === undefined) return null;
|
|
330
336
|
if (plan.alg === 'entity-select' || plan.alg === 'entity-join') {
|
|
331
337
|
if (plan.aggregate !== null) return null;
|
|
332
|
-
|
|
338
|
+
const bindings = plan.bindings;
|
|
339
|
+
const ties = bindings.flatMap((binding) => binding.keys === undefined
|
|
340
|
+
? [identityTerm(binding.name)] : binding.keys.map((column) => ({
|
|
341
|
+
source: 'column', binding: binding.name, column, path: null,
|
|
342
|
+
desc: false, nullsFirst: true, tieBreaker: true,
|
|
343
|
+
})));
|
|
344
|
+
if (plan.group) {
|
|
345
|
+
const group = plan.group;
|
|
346
|
+
const terms = group.order === 'first-seen' ? [] : group.order.map((term) => ({
|
|
347
|
+
source: 'group', binding: bindings[0].name,
|
|
348
|
+
column: term.aggregate === undefined ? group.keys[term.index].as : `a${term.aggregate}`,
|
|
349
|
+
path: null, desc: term.desc, nullsFirst: term.nullsFirst, tieBreaker: false,
|
|
350
|
+
}));
|
|
351
|
+
return [...terms, ...ties.map((term) => ({ ...term, source: 'group',
|
|
352
|
+
column: term.column, tieBreaker: group.order !== 'first-seen' }))];
|
|
353
|
+
}
|
|
354
|
+
return [...(plan.order ?? []).map((term) => declaredTerm(term, term.binding)), ...ties];
|
|
333
355
|
}
|
|
334
356
|
if (plan.aggregate !== null || plan.rank !== null) return null;
|
|
335
357
|
if (plan.bucket !== null) {
|