@fougere/adapter-sql 0.3.0-alpha.0 → 0.5.0-alpha.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 +5 -3
- package/dist/crud.d.ts +3 -3
- package/dist/ddl.d.ts.map +1 -1
- package/dist/ddl.js +2 -2
- package/dist/ddl.js.map +1 -1
- package/dist/dialect.d.ts +8 -0
- package/dist/dialect.d.ts.map +1 -1
- package/dist/dialect.js +10 -0
- package/dist/dialect.js.map +1 -1
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +2 -2
- package/dist/diff.js.map +1 -1
- package/dist/fields.d.ts +22 -0
- package/dist/fields.d.ts.map +1 -0
- package/dist/fields.js +2 -0
- package/dist/fields.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/query.d.ts +34 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +44 -0
- package/dist/query.js.map +1 -0
- package/dist/setup.d.ts +8 -0
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +2 -1
- package/dist/setup.js.map +1 -1
- package/dist/sqlite.d.ts.map +1 -1
- package/dist/sqlite.js +2 -1
- package/dist/sqlite.js.map +1 -1
- package/dist/step.js +3 -3
- package/dist/step.js.map +1 -1
- package/dist/table.d.ts +18 -22
- package/dist/table.d.ts.map +1 -1
- package/dist/table.js +40 -41
- package/dist/table.js.map +1 -1
- package/package.json +7 -5
- package/src/check.ts +76 -0
- package/src/crud.ts +570 -0
- package/src/ddl.ts +242 -0
- package/src/dialect.ts +204 -0
- package/src/diff.ts +196 -0
- package/src/fields.ts +24 -0
- package/src/index.ts +41 -0
- package/src/query.ts +64 -0
- package/src/setup.ts +72 -0
- package/src/sqlite.ts +44 -0
- package/src/step.ts +287 -0
- package/src/table.ts +428 -0
- package/src/values.ts +105 -0
package/dist/table.d.ts
CHANGED
|
@@ -3,18 +3,22 @@
|
|
|
3
3
|
*
|
|
4
4
|
* This is the neutral middle term: one projection reads the entity's axes and
|
|
5
5
|
* produces a `TableDef`; a `Dialect` turns that into SQL. Neither half knows the
|
|
6
|
-
* other — the
|
|
7
|
-
*
|
|
6
|
+
* other — the dialect never mentions a field. Adding a dialect touches only the
|
|
7
|
+
* second half.
|
|
8
|
+
*
|
|
9
|
+
* `ColumnDef.stated` is the one member the axes did not produce. It names one engine's
|
|
10
|
+
* column type, so dropping it leaves every column describable.
|
|
8
11
|
*/
|
|
9
|
-
import { type
|
|
12
|
+
import { type SchemaOrCard } from '@fougere/schema';
|
|
10
13
|
import { type ShapeBounds } from './check.js';
|
|
14
|
+
import type { SqlField } from './fields.js';
|
|
11
15
|
/** The shape keywords a dialect needs to choose a column type. */
|
|
12
16
|
export interface ColumnShape {
|
|
13
17
|
type?: string;
|
|
14
18
|
format?: string;
|
|
15
19
|
maxLength?: number;
|
|
16
20
|
}
|
|
17
|
-
/** One column, described by the axes —
|
|
21
|
+
/** One column, described by the axes — plus, at most, what the entity stated for sql. */
|
|
18
22
|
export interface ColumnDef {
|
|
19
23
|
/** Field key on the entity. */
|
|
20
24
|
field: string;
|
|
@@ -37,6 +41,11 @@ export interface ColumnDef {
|
|
|
37
41
|
bounds?: ShapeBounds;
|
|
38
42
|
/** The FK target, from `role.relation` when it's a `ref()` (kind `'one'`). */
|
|
39
43
|
references?: ColumnReference;
|
|
44
|
+
/**
|
|
45
|
+
* What the entity stated for THIS adapter — never an axis. It says how the column is
|
|
46
|
+
* realized here; drop it and the column is still describable.
|
|
47
|
+
*/
|
|
48
|
+
stated?: SqlField;
|
|
40
49
|
}
|
|
41
50
|
export interface ColumnReference {
|
|
42
51
|
table: string;
|
|
@@ -62,7 +71,7 @@ export interface RelationResolve {
|
|
|
62
71
|
/** Same resolver used for every entity's own table (default or a custom `tableName`). */
|
|
63
72
|
resolve: (name: string) => string;
|
|
64
73
|
/** Live entity class → its already-resolved table name, reused instead of re-derived. */
|
|
65
|
-
tableNameOf?: Map<
|
|
74
|
+
tableNameOf?: Map<SchemaOrCard, string>;
|
|
66
75
|
/** Which entities this batch holds and which live in another source — decided by NAME. */
|
|
67
76
|
hosted?: HostedNames;
|
|
68
77
|
}
|
|
@@ -82,10 +91,10 @@ export interface HostedNames {
|
|
|
82
91
|
*
|
|
83
92
|
* A lone card has no live relation targets, so a `ref()` falls back to the conventions
|
|
84
93
|
* `referenceFor`/`primaryColumnOf` already document (name-derived table, `id` as the key).
|
|
85
|
-
* Pass a
|
|
94
|
+
* Pass a descriptor through `Bundle.toSchemas` first when the FKs matter — it resolves the
|
|
86
95
|
* targets, and its output is the live-class case again.
|
|
87
96
|
*/
|
|
88
|
-
export declare function toTable(tableName: string, entity:
|
|
97
|
+
export declare function toTable(tableName: string, entity: SchemaOrCard, relations?: RelationResolve): TableDef;
|
|
89
98
|
/**
|
|
90
99
|
* Is this column part of a key? MySQL and SQL Server refuse an unbounded text
|
|
91
100
|
* column in a primary key or an index, so the dialect needs to know.
|
|
@@ -100,7 +109,7 @@ export declare function toTableName(name: string): string;
|
|
|
100
109
|
export interface EntityEntry {
|
|
101
110
|
name: string;
|
|
102
111
|
/** A live class in-process, a card from a frond whose class never crossed. */
|
|
103
|
-
entityClass:
|
|
112
|
+
entityClass: SchemaOrCard;
|
|
104
113
|
}
|
|
105
114
|
export interface FrondLike {
|
|
106
115
|
name: string;
|
|
@@ -110,7 +119,7 @@ export interface AppLike {
|
|
|
110
119
|
fronds: FrondLike[];
|
|
111
120
|
/** Auth runtime entities are migrated alongside scanned fronds when present. */
|
|
112
121
|
auth?: {
|
|
113
|
-
entities: Record<string,
|
|
122
|
+
entities: Record<string, SchemaOrCard>;
|
|
114
123
|
};
|
|
115
124
|
/**
|
|
116
125
|
* Entities this app hosts in ANOTHER source — named so a miss can be read.
|
|
@@ -120,19 +129,6 @@ export interface AppLike {
|
|
|
120
129
|
* one source, where a miss can only be a mistake.
|
|
121
130
|
*/
|
|
122
131
|
elsewhere?: string[];
|
|
123
|
-
/**
|
|
124
|
-
* The DERIVATIONS this app stores — registration names.
|
|
125
|
-
*
|
|
126
|
-
* A derivation makes no table by default: `Post.pick('id','title')` describes an
|
|
127
|
-
* answer, not a place rows live, and one dropped under `entities/` used to get a
|
|
128
|
-
* table of its own — measured on a real app, where a projection of an archived
|
|
129
|
-
* entity created a duplicate in the OTHER database.
|
|
130
|
-
*
|
|
131
|
-
* Naming it in `sources:` is the opt-in, and it changes what the thing is: a stored
|
|
132
|
-
* derivation is a dated COPY, not a projection, and it owes what any copy owes —
|
|
133
|
-
* who fills it, and how old it is.
|
|
134
|
-
*/
|
|
135
|
-
materialize?: string[];
|
|
136
132
|
}
|
|
137
133
|
/**
|
|
138
134
|
* Every entity an app hosts, as FK-aware tables — the shared middle step behind
|
package/dist/table.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../src/table.ts"],"names":[],"mappings":"AACA
|
|
1
|
+
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../src/table.ts"],"names":[],"mappings":"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"}
|
package/dist/table.js
CHANGED
|
@@ -4,10 +4,13 @@ import { Lifecycle, Role } from '@fougere/schema';
|
|
|
4
4
|
*
|
|
5
5
|
* This is the neutral middle term: one projection reads the entity's axes and
|
|
6
6
|
* produces a `TableDef`; a `Dialect` turns that into SQL. Neither half knows the
|
|
7
|
-
* other — the
|
|
8
|
-
*
|
|
7
|
+
* other — the dialect never mentions a field. Adding a dialect touches only the
|
|
8
|
+
* second half.
|
|
9
|
+
*
|
|
10
|
+
* `ColumnDef.stated` is the one member the axes did not produce. It names one engine's
|
|
11
|
+
* column type, so dropping it leaves every column describable.
|
|
9
12
|
*/
|
|
10
|
-
import { Anatomy, FieldGroup, Unique, fieldsOf,
|
|
13
|
+
import { Anatomy, FieldGroup, Unique, fieldsOf, lowerFirst, schemaOf } from '@fougere/schema';
|
|
11
14
|
import { boundsOf } from './check.js';
|
|
12
15
|
/** camelCase → snake_case */
|
|
13
16
|
export function toSnakeCase(str) {
|
|
@@ -22,8 +25,7 @@ function isStored(field) {
|
|
|
22
25
|
}
|
|
23
26
|
/**
|
|
24
27
|
* The target's primary key column. A live thunk (an in-process entity) answers
|
|
25
|
-
* for real; a relation reconstructed from a lone
|
|
26
|
-
* bundle — `packages/schema/src/projections/card.ts:18-22`) has lost it to a
|
|
28
|
+
* for real; a relation reconstructed from a lone `Card` (without a `Bundle`) has lost it to a
|
|
27
29
|
* name stand-in with no `getFields` — the convention there is to assume `id`.
|
|
28
30
|
*/
|
|
29
31
|
function primaryColumnOf(target) {
|
|
@@ -73,7 +75,7 @@ function referenceFor(field, resolve, tableNameOf, hosted) {
|
|
|
73
75
|
// `ref(Subscription)` while the table was in the very batch being built. Everything
|
|
74
76
|
// else that resolves a relation target already resolves it by name, for the same
|
|
75
77
|
// reason — a target rebuilt from a card is a `{ name }` stand-in.
|
|
76
|
-
const key =
|
|
78
|
+
const key = lowerFirst(target.name ?? '');
|
|
77
79
|
if (hosted.elsewhere.has(key))
|
|
78
80
|
return undefined;
|
|
79
81
|
if (!hosted.here.has(key)) {
|
|
@@ -81,11 +83,11 @@ function referenceFor(field, resolve, tableNameOf, hosted) {
|
|
|
81
83
|
`Check the entity is scanned, and that \`sources\` spells its name the same way.`);
|
|
82
84
|
}
|
|
83
85
|
}
|
|
84
|
-
const table = mapped ?? resolve(
|
|
86
|
+
const table = mapped ?? resolve(lowerFirst(target.name ?? ''));
|
|
85
87
|
const column = primaryColumnOf(target);
|
|
86
88
|
return relation.onDelete ? { table, column, onDelete: relation.onDelete } : { table, column };
|
|
87
89
|
}
|
|
88
|
-
function toColumn(fieldName, field, resolve, tableNameOf, hosted) {
|
|
90
|
+
function toColumn(fieldName, field, resolve, tableNameOf, hosted, stated) {
|
|
89
91
|
// The column type comes from the `shape` axis alone. `anatomy` strips the
|
|
90
92
|
// nullable union so a nullable integer stays an integer instead of falling
|
|
91
93
|
// through to text.
|
|
@@ -116,6 +118,8 @@ function toColumn(fieldName, field, resolve, tableNameOf, hosted) {
|
|
|
116
118
|
const references = referenceFor(field, resolve, tableNameOf, hosted);
|
|
117
119
|
if (references)
|
|
118
120
|
column.references = references;
|
|
121
|
+
if (stated)
|
|
122
|
+
column.stated = stated;
|
|
119
123
|
return column;
|
|
120
124
|
}
|
|
121
125
|
/**
|
|
@@ -127,17 +131,19 @@ function toColumn(fieldName, field, resolve, tableNameOf, hosted) {
|
|
|
127
131
|
*
|
|
128
132
|
* A lone card has no live relation targets, so a `ref()` falls back to the conventions
|
|
129
133
|
* `referenceFor`/`primaryColumnOf` already document (name-derived table, `id` as the key).
|
|
130
|
-
* Pass a
|
|
134
|
+
* Pass a descriptor through `Bundle.toSchemas` first when the FKs matter — it resolves the
|
|
131
135
|
* targets, and its output is the live-class case again.
|
|
132
136
|
*/
|
|
133
137
|
export function toTable(tableName, entity, relations) {
|
|
134
138
|
const resolve = relations?.resolve ?? toTableName;
|
|
135
139
|
const fields = fieldsOf(entity);
|
|
140
|
+
// Read off the entity, since that is where it is declared and addressed by field key.
|
|
141
|
+
const stated = schemaOf(entity).getAdapters()?.sql;
|
|
136
142
|
const columns = [];
|
|
137
143
|
for (const [fieldName, field] of Object.entries(fields)) {
|
|
138
144
|
if (!isStored(field))
|
|
139
145
|
continue;
|
|
140
|
-
columns.push(toColumn(fieldName, field, resolve, relations?.tableNameOf, relations?.hosted));
|
|
146
|
+
columns.push(toColumn(fieldName, field, resolve, relations?.tableNameOf, relations?.hosted, stated?.[fieldName]));
|
|
141
147
|
}
|
|
142
148
|
const primaries = columns.filter((column) => column.primary).map((column) => column.name);
|
|
143
149
|
const stored = new Set(columns.map((column) => column.name));
|
|
@@ -184,47 +190,41 @@ export function toTableName(name) {
|
|
|
184
190
|
return name.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`) + 's';
|
|
185
191
|
}
|
|
186
192
|
/**
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
193
|
+
* A schema says whether it holds rows; this adapter decides what to emit for it.
|
|
194
|
+
*
|
|
195
|
+
* A root always did. A derivation describes ANOTHER's rows — `Post.pick('id','title')` is
|
|
196
|
+
* a shape, and one dropped under `entities/` used to get a table of its own: measured on a
|
|
197
|
+
* real app, where a projection of an archived entity created a duplicate in the OTHER
|
|
198
|
+
* database. `.anchor()` is what says otherwise, and it is the entity's own word, never a
|
|
199
|
+
* key of the app's config: a frond stays mountable without its host knowing.
|
|
190
200
|
*/
|
|
191
|
-
function
|
|
192
|
-
|
|
201
|
+
function verdictOn(entry) {
|
|
202
|
+
const schema = schemaOf(entry.entityClass);
|
|
203
|
+
return !schema.derivation || schema.anchored ? 'table' : 'answer';
|
|
193
204
|
}
|
|
194
205
|
/**
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
* design exists to refuse: rows from yesterday typed exactly like rows from now. The
|
|
199
|
-
* vocabulary already carries the answer — a field with `update: 'now'` records when
|
|
200
|
-
* this row last changed HERE, which for a copy is when it was last pulled. So nothing
|
|
201
|
-
* new is declared; what is new is that forgetting it is refused, at boot, by name.
|
|
202
|
-
*
|
|
203
|
-
* An entity is untouched: it is not a copy of anything, and its rows are the truth.
|
|
206
|
+
* Every entity this app hosts, once each. The same class reaches here under one
|
|
207
|
+
* registration name from two lists — a frond's `entities/` and the auth runtime's map —
|
|
208
|
+
* and two entries for one name are two CREATE TABLE for one table.
|
|
204
209
|
*/
|
|
205
|
-
function refuseUndated(name, source) {
|
|
206
|
-
const dated = Object.values(fieldsOf(source)).some((field) => Lifecycle.of(field).stampedOnUpdate);
|
|
207
|
-
if (dated)
|
|
208
|
-
return;
|
|
209
|
-
throw new Error(`${name} is stored as a derivation but carries no \`updated()\` field — a copy that ` +
|
|
210
|
-
`cannot say when it was pulled reads exactly like live rows. Add one, or drop it from \`sources\`.`);
|
|
211
|
-
}
|
|
212
210
|
function collectEntities(app) {
|
|
213
|
-
const stored = new Set((app.materialize ?? []).map((name) => registrationKeyOf(name)));
|
|
214
211
|
const entries = [];
|
|
212
|
+
const held = new Set();
|
|
213
|
+
const hold = (entry) => {
|
|
214
|
+
if (held.has(lowerFirst(entry.name)))
|
|
215
|
+
return;
|
|
216
|
+
held.add(lowerFirst(entry.name));
|
|
217
|
+
entries.push(entry);
|
|
218
|
+
};
|
|
215
219
|
for (const frond of app.fronds) {
|
|
216
220
|
for (const entry of frond.entities) {
|
|
217
|
-
if (
|
|
218
|
-
|
|
219
|
-
continue;
|
|
220
|
-
refuseUndated(entry.name, entry.entityClass);
|
|
221
|
-
}
|
|
222
|
-
entries.push(entry);
|
|
221
|
+
if (verdictOn(entry) === 'table')
|
|
222
|
+
hold(entry);
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
if (app.auth?.entities) {
|
|
226
226
|
for (const [name, entityClass] of Object.entries(app.auth.entities))
|
|
227
|
-
|
|
227
|
+
hold({ name, entityClass });
|
|
228
228
|
}
|
|
229
229
|
return entries;
|
|
230
230
|
}
|
|
@@ -237,9 +237,8 @@ function collectEntities(app) {
|
|
|
237
237
|
export function toTables(app, resolve) {
|
|
238
238
|
const entries = collectEntities(app);
|
|
239
239
|
const tableNameOf = new Map(entries.map((entry) => [entry.entityClass, resolve(entry.name)]));
|
|
240
|
-
const lower = (name) => name.charAt(0).toLowerCase() + name.slice(1);
|
|
241
240
|
const hosted = app.elsewhere
|
|
242
|
-
? { here: new Set(entries.map((entry) =>
|
|
241
|
+
? { here: new Set(entries.map((entry) => lowerFirst(entry.name))), elsewhere: new Set(app.elsewhere.map(lowerFirst)) }
|
|
243
242
|
: undefined;
|
|
244
243
|
return entries.map((entry) => toTable(resolve(entry.name), entry.entityClass, { resolve, tableNameOf, hosted }));
|
|
245
244
|
}
|
package/dist/table.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.js","sourceRoot":"","sources":["../src/table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAClD
|
|
1
|
+
{"version":3,"file":"table.js","sourceRoot":"","sources":["../src/table.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fougere/adapter-sql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0-alpha.0",
|
|
4
4
|
"description": "Entity → SQL tables through Kysely (SQLite, PostgreSQL, MySQL, SQL Server).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fougere",
|
|
@@ -33,12 +33,14 @@
|
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"files": [
|
|
36
|
-
"dist"
|
|
36
|
+
"dist",
|
|
37
|
+
"src"
|
|
37
38
|
],
|
|
38
39
|
"dependencies": {
|
|
39
40
|
"better-sqlite3": "^13.0.3",
|
|
40
|
-
"
|
|
41
|
-
"@fougere/core": "0.
|
|
41
|
+
"dequal": "^2.0.3",
|
|
42
|
+
"@fougere/core": "0.5.0-alpha.0",
|
|
43
|
+
"@fougere/schema": "0.5.0-alpha.0"
|
|
42
44
|
},
|
|
43
45
|
"peerDependencies": {
|
|
44
46
|
"kysely": ">=0.28.17"
|
|
@@ -47,7 +49,7 @@
|
|
|
47
49
|
"@types/better-sqlite3": "^7.6.13",
|
|
48
50
|
"@types/node": "^22.0.0",
|
|
49
51
|
"kysely": "^0.28.17",
|
|
50
|
-
"@fougere/core": "0.
|
|
52
|
+
"@fougere/core": "0.5.0-alpha.0"
|
|
51
53
|
},
|
|
52
54
|
"publishConfig": {
|
|
53
55
|
"access": "public"
|
package/src/check.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shape → CHECK constraints.
|
|
3
|
+
*
|
|
4
|
+
* `oneOf`, `min`, `max` were declared on the field and read by the façade alone: a
|
|
5
|
+
* handler writing through the ORM put `status: 'brouillon'` in a column that declares
|
|
6
|
+
* two values, and nothing said a word. The rule was in the schema; no one held it on
|
|
7
|
+
* that path.
|
|
8
|
+
*
|
|
9
|
+
* So the storage learns what the shape already says. `validate` judges what a client
|
|
10
|
+
* proposes; this holds what anyone writes — including us.
|
|
11
|
+
*
|
|
12
|
+
* Only the keywords a database can decide alone. `pattern` and `format` are left to
|
|
13
|
+
* the façade: regex dialects diverge (POSIX here, PCRE there, nothing in SQLite
|
|
14
|
+
* without an extension), and a constraint that means something different per engine
|
|
15
|
+
* is worse than none.
|
|
16
|
+
*
|
|
17
|
+
* Every value is inlined with `sql.lit`, not bound: SQLite answers `parameters
|
|
18
|
+
* prohibited in CHECK constraints`, and a constraint is part of the schema rather
|
|
19
|
+
* than of a query. The values are the author's own literals — `oneOf('draft', …)`,
|
|
20
|
+
* `max: 160` — never anything a request carried, and Kysely escapes them.
|
|
21
|
+
*/
|
|
22
|
+
import { sql, type Expression, type SqlBool } from 'kysely';
|
|
23
|
+
import type { ColumnDef } from './table.js';
|
|
24
|
+
|
|
25
|
+
/** What a column's shape can be checked for, beyond its type. */
|
|
26
|
+
export interface ShapeBounds {
|
|
27
|
+
enum?: readonly (string | number)[];
|
|
28
|
+
minLength?: number;
|
|
29
|
+
maxLength?: number;
|
|
30
|
+
minimum?: number;
|
|
31
|
+
maximum?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The CHECK expression for one column, or nothing when its shape bounds nothing.
|
|
36
|
+
*
|
|
37
|
+
* A nullable column passes when it holds `null`: `NOT NULL` is the axis that decides
|
|
38
|
+
* presence, and stacking the two would make `optional()` unwritable.
|
|
39
|
+
*/
|
|
40
|
+
export function checkFor(column: ColumnDef): Expression<SqlBool> | undefined {
|
|
41
|
+
const bounds = column.bounds;
|
|
42
|
+
if (!bounds) return undefined;
|
|
43
|
+
|
|
44
|
+
const col = sql.ref(column.name);
|
|
45
|
+
const terms: Expression<SqlBool>[] = [];
|
|
46
|
+
|
|
47
|
+
if (bounds.enum?.length) {
|
|
48
|
+
terms.push(sql<SqlBool>`${col} in (${sql.join(bounds.enum.map((v) => sql.lit(v)))})`);
|
|
49
|
+
}
|
|
50
|
+
// Length, not value: a text bound is about the string, and `maxLength` is already
|
|
51
|
+
// spent on the column type where the dialect uses it (varchar(n)) — stating it
|
|
52
|
+
// again costs nothing and holds where the type does not (text columns).
|
|
53
|
+
if (bounds.minLength !== undefined) terms.push(sql<SqlBool>`length(${col}) >= ${sql.lit(bounds.minLength)}`);
|
|
54
|
+
if (bounds.maxLength !== undefined) terms.push(sql<SqlBool>`length(${col}) <= ${sql.lit(bounds.maxLength)}`);
|
|
55
|
+
if (bounds.minimum !== undefined) terms.push(sql<SqlBool>`${col} >= ${sql.lit(bounds.minimum)}`);
|
|
56
|
+
if (bounds.maximum !== undefined) terms.push(sql<SqlBool>`${col} <= ${sql.lit(bounds.maximum)}`);
|
|
57
|
+
|
|
58
|
+
if (terms.length === 0) return undefined;
|
|
59
|
+
|
|
60
|
+
const all = terms.reduce((left, right) => sql<SqlBool>`${left} and ${right}`);
|
|
61
|
+
|
|
62
|
+
return column.nullable ? sql<SqlBool>`${col} is null or (${all})` : all;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Read the bounds off a field's base shape — the keywords a database can decide. */
|
|
66
|
+
export function boundsOf(shape: Record<string, unknown> | undefined): ShapeBounds | undefined {
|
|
67
|
+
if (!shape) return undefined;
|
|
68
|
+
|
|
69
|
+
const bounds: ShapeBounds = {};
|
|
70
|
+
if (Array.isArray(shape.enum) && shape.enum.length > 0) bounds.enum = shape.enum as string[];
|
|
71
|
+
for (const key of ['minLength', 'maxLength', 'minimum', 'maximum'] as const) {
|
|
72
|
+
if (typeof shape[key] === 'number') bounds[key] = shape[key] as number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return Object.keys(bounds).length > 0 ? bounds : undefined;
|
|
76
|
+
}
|