qubu 0.0.0 → 0.3.4
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/dist/canonical-BbnqavJm.mjs +972 -0
- package/dist/codegen.d.mts +107 -0
- package/dist/codegen.mjs +1199 -0
- package/dist/column-CXMxx8Hq.mjs +118 -0
- package/dist/complete-D5Djh-zo.mjs +1788 -0
- package/dist/complete-types-BdFqUfbb.d.mts +371 -0
- package/dist/core.d.mts +33 -0
- package/dist/core.mjs +36 -0
- package/dist/ddl.d.mts +118 -0
- package/dist/ddl.mjs +1120 -0
- package/dist/dialect-b2-Z6uBF.mjs +13 -0
- package/dist/diff.d.mts +2 -0
- package/dist/diff.mjs +883 -0
- package/dist/drizzle-mysql.d.mts +24 -0
- package/dist/drizzle-mysql.mjs +72 -0
- package/dist/drizzle-postgres.d.mts +24 -0
- package/dist/drizzle-postgres.mjs +73 -0
- package/dist/drizzle-sqlite.d.mts +24 -0
- package/dist/drizzle-sqlite.mjs +82 -0
- package/dist/drizzle.d.mts +13 -0
- package/dist/drizzle.mjs +2 -0
- package/dist/errors-BGCoLe_r.mjs +14 -0
- package/dist/errors-Dxv73YJu.mjs +26 -0
- package/dist/explain-CkIK13L_.mjs +98 -0
- package/dist/index-Ds7-mhJi.d.mts +146 -0
- package/dist/index-Dug5HnLB.d.mts +204 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +1276 -0
- package/dist/introspection.d.mts +117 -0
- package/dist/introspection.mjs +4341 -0
- package/dist/json-CUZlv4HT.mjs +169 -0
- package/dist/migration.d.mts +215 -0
- package/dist/migration.mjs +1161 -0
- package/dist/mysql-DqkqXB6A.mjs +355 -0
- package/dist/mysql.d.mts +5 -0
- package/dist/mysql.mjs +50 -0
- package/dist/naming-QVCOnSj2.mjs +20 -0
- package/dist/omit-OxV58AwX.mjs +5 -0
- package/dist/on-conflict-BxnxubMb.mjs +143 -0
- package/dist/postgres-DEBBeh52.mjs +235 -0
- package/dist/postgres.d.mts +123 -0
- package/dist/postgres.mjs +52 -0
- package/dist/registry-BufIskVN.mjs +569 -0
- package/dist/relational-DCZrrNia.mjs +77 -0
- package/dist/runtime-BTr-MTlo.mjs +193 -0
- package/dist/schema.d.mts +2 -0
- package/dist/schema.mjs +5 -0
- package/dist/serialize-PF1cfH2P.mjs +556 -0
- package/dist/snapshot-CWPgzxNx.mjs +392 -0
- package/dist/snapshot.d.mts +4 -0
- package/dist/snapshot.mjs +8 -0
- package/dist/source-DUoJVXmL.mjs +68 -0
- package/dist/sqlite-BU6DBxef.mjs +320 -0
- package/dist/sqlite.d.mts +5 -0
- package/dist/sqlite.mjs +48 -0
- package/dist/standard-BTVYKh_F.mjs +12 -0
- package/dist/table-llv9tsZ8.mjs +384 -0
- package/dist/types-4Q076HKo.d.mts +3636 -0
- package/dist/types-BX0mckiU.d.mts +644 -0
- package/dist/types-Cec0xzo4.mjs +116 -0
- package/dist/types-H4vyCw8_.d.mts +45 -0
- package/dist/value-BvilP0oz.mjs +29 -0
- package/dist/vite/ambient.d.ts +449 -0
- package/dist/vite.d.mts +35 -0
- package/dist/vite.mjs +498 -0
- package/docs/config.json +21 -0
- package/docs/dialects-and-execution.md +427 -0
- package/docs/getting-started.md +96 -0
- package/docs/guides/compose-queries.md +225 -0
- package/docs/guides/drizzle.md +130 -0
- package/docs/guides/extensions/dialects.md +40 -0
- package/docs/guides/extensions/overview.md +36 -0
- package/docs/guides/extensions/sources-and-clauses.md +82 -0
- package/docs/guides/extensions/typed-expressions.md +94 -0
- package/docs/guides/extensions/unsafe-syntax.md +27 -0
- package/docs/guides/json.md +65 -0
- package/docs/guides/mutations.md +138 -0
- package/docs/guides/select/conditions.md +110 -0
- package/docs/guides/select/grouping-and-windows.md +82 -0
- package/docs/guides/select/ordering-and-pagination.md +78 -0
- package/docs/guides/select/overview.md +139 -0
- package/docs/guides/sql-templates.md +157 -0
- package/docs/guides/vite-plugin.md +92 -0
- package/docs/index.md +111 -0
- package/docs/query-model/fragments.md +109 -0
- package/docs/query-model/result-shapes.md +125 -0
- package/docs/query-model/source-scope.md +182 -0
- package/docs/reference/introspection-support.md +196 -0
- package/docs/reference/mysql-snapshot.md +55 -0
- package/docs/reference/postgres-snapshot.md +61 -0
- package/docs/reference/sqlite-snapshot.md +53 -0
- package/docs/reference/supported-surface.md +104 -0
- package/docs/schema/catalog-model.md +58 -0
- package/docs/schema/code-generation.md +139 -0
- package/docs/schema/columns-and-writes.md +130 -0
- package/docs/schema/constraints-and-indexes.md +180 -0
- package/docs/schema/ddl-emission.md +75 -0
- package/docs/schema/diff.md +75 -0
- package/docs/schema/introspection.md +214 -0
- package/docs/schema/migration-plans.md +76 -0
- package/docs/schema/snapshots.md +93 -0
- package/docs/schema/storage-and-schema-sql.md +104 -0
- package/docs/schema/tables-and-names.md +106 -0
- package/docs/sql-semantic-types.md +141 -0
- package/docs/troubleshooting.md +151 -0
- package/package.json +132 -10
- package/skills/qubu/SKILL.md +42 -0
- package/skills/qubu/agents/openai.yaml +6 -0
- package/readme.md +0 -1
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { r as isColumnReference } from "./column-CXMxx8Hq.mjs";
|
|
2
|
+
import { A as isValidSchemaObjectName, _ as isUnsafeSchemaSql, m as validateConstraintDialect, w as validateIndexDialect, y as renderSchemaExpression } from "./registry-BufIskVN.mjs";
|
|
3
|
+
import { t as createSchemaDialect } from "./dialect-b2-Z6uBF.mjs";
|
|
4
|
+
import { a as toSnapshotJsonValue } from "./canonical-BbnqavJm.mjs";
|
|
5
|
+
import { n as createSchemaSnapshot, s as tryCreateSchemaSnapshot } from "./serialize-PF1cfH2P.mjs";
|
|
6
|
+
import { mysqlDialect } from "./mysql.mjs";
|
|
7
|
+
//#region src/snapshot/mysql.ts
|
|
8
|
+
/** MySQL's v1 snapshot extension identity. */
|
|
9
|
+
const mysqlSnapshotDialect = Object.freeze({
|
|
10
|
+
name: "mysql",
|
|
11
|
+
version: 1
|
|
12
|
+
});
|
|
13
|
+
var MysqlSnapshotDialectError = class extends TypeError {
|
|
14
|
+
code = "dialect-mismatch";
|
|
15
|
+
};
|
|
16
|
+
const mysqlStorageTypes = Object.freeze({
|
|
17
|
+
integer: "INT",
|
|
18
|
+
numeric: "DECIMAL",
|
|
19
|
+
text: "TEXT",
|
|
20
|
+
boolean: "BOOLEAN",
|
|
21
|
+
date: "DATE",
|
|
22
|
+
timestamp: "DATETIME",
|
|
23
|
+
uuid: "CHAR(36)",
|
|
24
|
+
json: "JSON",
|
|
25
|
+
bigint: "BIGINT",
|
|
26
|
+
binary: "VARBINARY"
|
|
27
|
+
});
|
|
28
|
+
/** MySQL's query dialect plus its schema metadata behavior. */
|
|
29
|
+
const mysqlSchemaDialect = createSchemaDialect(mysqlDialect(), {
|
|
30
|
+
version: 1,
|
|
31
|
+
validate: validateMysqlSchema,
|
|
32
|
+
encodeStorage(storage, context) {
|
|
33
|
+
return encodeMysqlStorage(storage, context.dialect);
|
|
34
|
+
},
|
|
35
|
+
encodeExpression(expression, context) {
|
|
36
|
+
return encodeMysqlExpression(expression, context.mode, context.dialect);
|
|
37
|
+
},
|
|
38
|
+
encodeDialectExtension(extension, context) {
|
|
39
|
+
return encodeMysqlExtension(extension, context.dialect);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
/** Snapshot adapter retaining the historical adapter-shaped entry point. */
|
|
43
|
+
const mysqlSnapshotAdapter = Object.freeze({ dialect: mysqlSchemaDialect });
|
|
44
|
+
/** Create a canonical MySQL schema snapshot. */
|
|
45
|
+
function createMysqlSchemaSnapshot(schema, options = {}) {
|
|
46
|
+
return createSchemaSnapshot(schema, {
|
|
47
|
+
...options,
|
|
48
|
+
adapter: mysqlSnapshotAdapter
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/** Return MySQL capability diagnostics without throwing. */
|
|
52
|
+
function tryCreateMysqlSchemaSnapshot(schema, options = {}) {
|
|
53
|
+
return tryCreateSchemaSnapshot(schema, {
|
|
54
|
+
...options,
|
|
55
|
+
adapter: mysqlSnapshotAdapter
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function encodeMysqlStorage(storage, dialect) {
|
|
59
|
+
if (storage.kind === "native") {
|
|
60
|
+
if (storage.dialect !== dialect.name) throw new MysqlSnapshotDialectError(`MySQL storage cannot encode a native declaration owned by "${storage.dialect}"`);
|
|
61
|
+
if (storage.type.trim().length === 0) throw new TypeError("MySQL native storage declarations cannot be empty");
|
|
62
|
+
return {
|
|
63
|
+
kind: "native",
|
|
64
|
+
dialect: dialect.name,
|
|
65
|
+
type: storage.type
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const type = mysqlStorageTypes[storage.type];
|
|
69
|
+
if (type === void 0) throw new TypeError(`Unsupported portable storage type for MySQL: ${String(storage.type)}`);
|
|
70
|
+
return {
|
|
71
|
+
kind: "native",
|
|
72
|
+
dialect: dialect.name,
|
|
73
|
+
type
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function encodeMysqlExpression(expression, mode, dialect) {
|
|
77
|
+
if (isUnsafeSchemaSql(expression) && expression.schemaSqlDialect !== dialect.name) throw new MysqlSnapshotDialectError(`Schema SQL is tagged for "${expression.schemaSqlDialect}" but the MySQL schema dialect is "${dialect.name}"`);
|
|
78
|
+
const rendered = renderSchemaExpression(expression, {
|
|
79
|
+
mode,
|
|
80
|
+
dialect
|
|
81
|
+
});
|
|
82
|
+
if (rendered.parameters.length !== 0) throw new TypeError("MySQL schema expressions must be parameter-free");
|
|
83
|
+
return {
|
|
84
|
+
kind: "expression",
|
|
85
|
+
expressionKind: expression.expressionKind,
|
|
86
|
+
sql: rendered.text,
|
|
87
|
+
...isUnsafeSchemaSql(expression) ? { dialect: dialect.name } : {}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function encodeMysqlExtension(extension, dialect) {
|
|
91
|
+
if (extension.dialect !== dialect.name) throw new MysqlSnapshotDialectError(`MySQL schema extensions require dialect "${dialect.name}"`);
|
|
92
|
+
const data = Object.fromEntries(Object.entries(extension).filter(([key]) => key !== "dialect").sort(([left], [right]) => left.localeCompare(right)));
|
|
93
|
+
return {
|
|
94
|
+
dialect: dialect.name,
|
|
95
|
+
version: dialect.schema.version,
|
|
96
|
+
data: sortSnapshotJson(toSnapshotJsonValue(data))
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function sortSnapshotJson(value) {
|
|
100
|
+
if (Array.isArray(value)) return value.map(sortSnapshotJson);
|
|
101
|
+
if (typeof value !== "object" || value === null) return value;
|
|
102
|
+
return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, child]) => [key, sortSnapshotJson(child)]));
|
|
103
|
+
}
|
|
104
|
+
function validateMysqlSchema(schema, context) {
|
|
105
|
+
const diagnostics = [];
|
|
106
|
+
const tableEntries = Object.entries(schema.registry).sort(([left], [right]) => compareIds(left, right));
|
|
107
|
+
const relationNames = /* @__PURE__ */ new Map();
|
|
108
|
+
if (schema.namespace !== void 0) validateMysqlName(schema.namespace, ["namespace"], "MySQL database/schema", diagnostics);
|
|
109
|
+
for (const [id, entry] of tableEntries) {
|
|
110
|
+
const tablePath = ["tables", id];
|
|
111
|
+
validateMysqlName(entry.physicalName, [...tablePath, "physicalName"], "MySQL table", diagnostics);
|
|
112
|
+
addScopedName(relationNames, entry.physicalName, [...tablePath, "physicalName"], "table", diagnostics);
|
|
113
|
+
}
|
|
114
|
+
for (const [id, entry] of tableEntries) validateMysqlTable(id, entry, context, diagnostics);
|
|
115
|
+
return Object.freeze(diagnostics);
|
|
116
|
+
}
|
|
117
|
+
function validateMysqlTable(tableId, entry, context, diagnostics) {
|
|
118
|
+
const table = entry.table;
|
|
119
|
+
const tablePath = ["tables", tableId];
|
|
120
|
+
const definitions = table.definitions;
|
|
121
|
+
const metadata = table;
|
|
122
|
+
for (const [columnId, definition] of Object.entries(definitions).sort(([left], [right]) => compareIds(left, right))) {
|
|
123
|
+
const columnPath = [
|
|
124
|
+
...tablePath,
|
|
125
|
+
"columns",
|
|
126
|
+
columnId
|
|
127
|
+
];
|
|
128
|
+
validateMysqlName(table.sqlNames[columnId] ?? columnId, [...columnPath, "physicalName"], "MySQL column", diagnostics);
|
|
129
|
+
if (definition.onUpdate !== void 0 && definition.generatedColumn) diagnostics.push({
|
|
130
|
+
code: "unsupported-dialect-option",
|
|
131
|
+
message: "MySQL generated columns cannot also declare ON UPDATE",
|
|
132
|
+
path: [...columnPath, "onUpdate"]
|
|
133
|
+
});
|
|
134
|
+
const extension = definition.identity?.dialect;
|
|
135
|
+
const autoIncrement = extension?.dialect === mysqlSnapshotDialect.name ? extension.autoIncrement : void 0;
|
|
136
|
+
if (extension?.dialect === mysqlSnapshotDialect.name && autoIncrement !== void 0 && typeof autoIncrement !== "boolean") diagnostics.push({
|
|
137
|
+
code: "unsupported-dialect-option",
|
|
138
|
+
message: "MySQL identity autoIncrement must be boolean",
|
|
139
|
+
path: [
|
|
140
|
+
...columnPath,
|
|
141
|
+
"identity",
|
|
142
|
+
"dialect",
|
|
143
|
+
"autoIncrement"
|
|
144
|
+
]
|
|
145
|
+
});
|
|
146
|
+
if (autoIncrement === true) validateMysqlAutoIncrement(columnId, definition, metadata, columnPath, diagnostics);
|
|
147
|
+
}
|
|
148
|
+
const constraintNames = /* @__PURE__ */ new Map();
|
|
149
|
+
const constraints = Object.entries(metadata.constraints);
|
|
150
|
+
for (const [constraintId, constraint] of constraints.sort(([left], [right]) => compareIds(left, right))) {
|
|
151
|
+
const constraintPath = [
|
|
152
|
+
...tablePath,
|
|
153
|
+
"constraints",
|
|
154
|
+
constraintId
|
|
155
|
+
];
|
|
156
|
+
const physicalName = constraint.physicalName ?? constraintId;
|
|
157
|
+
validateMysqlName(physicalName, [...constraintPath, "physicalName"], "MySQL constraint", diagnostics);
|
|
158
|
+
addScopedName(constraintNames, physicalName, [...constraintPath, "physicalName"], "constraint", diagnostics);
|
|
159
|
+
appendMetadataDiagnostics(diagnostics, validateConstraintDialect(constraint, context.dialect.name, constraintPath));
|
|
160
|
+
if (constraint.kind === "foreign-key" && (constraint.match === "full" || constraint.match === "partial")) diagnostics.push({
|
|
161
|
+
code: "unsupported-dialect-option",
|
|
162
|
+
message: "MySQL v1 snapshots support MATCH SIMPLE foreign keys only",
|
|
163
|
+
path: [...constraintPath, "match"]
|
|
164
|
+
});
|
|
165
|
+
if (constraint.kind === "foreign-key" && (constraint.onUpdate === "set-default" || constraint.onDelete === "set-default")) diagnostics.push({
|
|
166
|
+
code: "unsupported-dialect-option",
|
|
167
|
+
message: "MySQL does not support SET DEFAULT foreign-key actions",
|
|
168
|
+
path: [...constraintPath, constraint.onUpdate === "set-default" ? "onUpdate" : "onDelete"]
|
|
169
|
+
});
|
|
170
|
+
if (constraint.kind === "unique-constraint" && constraint.nulls === "not-distinct") diagnostics.push({
|
|
171
|
+
code: "unsupported-dialect-option",
|
|
172
|
+
message: "MySQL ordinary UNIQUE constraints use distinct NULL semantics in v1",
|
|
173
|
+
path: [...constraintPath, "nulls"]
|
|
174
|
+
});
|
|
175
|
+
if (constraint.dialect?.dialect === mysqlSnapshotDialect.name && "enforced" in constraint.dialect && constraint.dialect.enforced !== void 0 && typeof constraint.dialect.enforced !== "boolean") diagnostics.push({
|
|
176
|
+
code: "unsupported-dialect-option",
|
|
177
|
+
message: "MySQL constraint enforced must be boolean",
|
|
178
|
+
path: [
|
|
179
|
+
...constraintPath,
|
|
180
|
+
"dialect",
|
|
181
|
+
"enforced"
|
|
182
|
+
]
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
const indexes = Object.entries(metadata.indexes);
|
|
186
|
+
const tableIndexNames = /* @__PURE__ */ new Map();
|
|
187
|
+
for (const [indexId, index] of indexes.sort(([left], [right]) => compareIds(left, right))) {
|
|
188
|
+
const indexPath = [
|
|
189
|
+
...tablePath,
|
|
190
|
+
"indexes",
|
|
191
|
+
indexId
|
|
192
|
+
];
|
|
193
|
+
const physicalName = index.physicalName ?? indexId;
|
|
194
|
+
validateMysqlName(physicalName, [...indexPath, "physicalName"], "MySQL index", diagnostics);
|
|
195
|
+
addScopedName(tableIndexNames, physicalName, [...indexPath, "physicalName"], "index", diagnostics);
|
|
196
|
+
appendMetadataDiagnostics(diagnostics, validateIndexDialect(index, context.dialect.name, indexPath));
|
|
197
|
+
if (index.predicate !== void 0) diagnostics.push({
|
|
198
|
+
code: "unsupported-dialect-option",
|
|
199
|
+
message: "MySQL does not support partial-index predicates",
|
|
200
|
+
path: [...indexPath, "predicate"]
|
|
201
|
+
});
|
|
202
|
+
for (const [termIndex, term] of index.terms.entries()) if (isOrderTerm(term) && term.nulls !== void 0) diagnostics.push({
|
|
203
|
+
code: "unsupported-dialect-option",
|
|
204
|
+
message: "MySQL index terms do not support NULLS FIRST/LAST syntax",
|
|
205
|
+
path: [
|
|
206
|
+
...indexPath,
|
|
207
|
+
"terms",
|
|
208
|
+
termIndex,
|
|
209
|
+
"nulls"
|
|
210
|
+
]
|
|
211
|
+
});
|
|
212
|
+
const extension = index.dialect;
|
|
213
|
+
if (extension?.dialect === mysqlSnapshotDialect.name) {
|
|
214
|
+
const mysqlExtension = extension;
|
|
215
|
+
if (mysqlExtension.algorithm !== void 0 && ![
|
|
216
|
+
"default",
|
|
217
|
+
"inplace",
|
|
218
|
+
"copy"
|
|
219
|
+
].includes(String(mysqlExtension.algorithm))) diagnostics.push({
|
|
220
|
+
code: "unsupported-dialect-option",
|
|
221
|
+
message: "MySQL index algorithm is invalid",
|
|
222
|
+
path: [
|
|
223
|
+
...indexPath,
|
|
224
|
+
"dialect",
|
|
225
|
+
"algorithm"
|
|
226
|
+
]
|
|
227
|
+
});
|
|
228
|
+
if (mysqlExtension.lock !== void 0 && ![
|
|
229
|
+
"default",
|
|
230
|
+
"none",
|
|
231
|
+
"shared",
|
|
232
|
+
"exclusive"
|
|
233
|
+
].includes(String(mysqlExtension.lock))) diagnostics.push({
|
|
234
|
+
code: "unsupported-dialect-option",
|
|
235
|
+
message: "MySQL index lock mode is invalid",
|
|
236
|
+
path: [
|
|
237
|
+
...indexPath,
|
|
238
|
+
"dialect",
|
|
239
|
+
"lock"
|
|
240
|
+
]
|
|
241
|
+
});
|
|
242
|
+
if (mysqlExtension.using !== void 0 && ![
|
|
243
|
+
"btree",
|
|
244
|
+
"hash",
|
|
245
|
+
"rtree"
|
|
246
|
+
].includes(String(mysqlExtension.using))) diagnostics.push({
|
|
247
|
+
code: "unsupported-dialect-option",
|
|
248
|
+
message: "MySQL index access method is invalid",
|
|
249
|
+
path: [
|
|
250
|
+
...indexPath,
|
|
251
|
+
"dialect",
|
|
252
|
+
"using"
|
|
253
|
+
]
|
|
254
|
+
});
|
|
255
|
+
if (mysqlExtension.parser !== void 0 && (typeof mysqlExtension.parser !== "string" || mysqlExtension.parser.trim().length === 0)) diagnostics.push({
|
|
256
|
+
code: "unsupported-dialect-option",
|
|
257
|
+
message: "MySQL index parser must be a non-empty string",
|
|
258
|
+
path: [
|
|
259
|
+
...indexPath,
|
|
260
|
+
"dialect",
|
|
261
|
+
"parser"
|
|
262
|
+
]
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function validateMysqlAutoIncrement(columnId, definition, metadata, columnPath, diagnostics) {
|
|
268
|
+
if (definition.nullable) diagnostics.push({
|
|
269
|
+
code: "unsupported-dialect-option",
|
|
270
|
+
message: "MySQL AUTO_INCREMENT columns must be non-nullable",
|
|
271
|
+
path: [...columnPath, "nullable"]
|
|
272
|
+
});
|
|
273
|
+
if (definition.generatedColumn !== void 0) diagnostics.push({
|
|
274
|
+
code: "unsupported-dialect-option",
|
|
275
|
+
message: "MySQL AUTO_INCREMENT columns cannot be generated columns",
|
|
276
|
+
path: [...columnPath, "generatedColumn"]
|
|
277
|
+
});
|
|
278
|
+
const declaration = mysqlDeclaration(definition.storage);
|
|
279
|
+
if (declaration === void 0 || !isMysqlIntegerDeclaration(declaration)) diagnostics.push({
|
|
280
|
+
code: "unsupported-dialect-option",
|
|
281
|
+
message: "MySQL AUTO_INCREMENT requires an integer-family physical storage declaration",
|
|
282
|
+
path: [...columnPath, "storage"]
|
|
283
|
+
});
|
|
284
|
+
if (!hasMysqlAutoIncrementKey(columnId, metadata)) diagnostics.push({
|
|
285
|
+
code: "unsupported-dialect-option",
|
|
286
|
+
message: "MySQL AUTO_INCREMENT columns must be the first column of a primary, unique, or ordinary index key",
|
|
287
|
+
path: [
|
|
288
|
+
...columnPath,
|
|
289
|
+
"identity",
|
|
290
|
+
"dialect",
|
|
291
|
+
"autoIncrement"
|
|
292
|
+
]
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
function mysqlDeclaration(storage) {
|
|
296
|
+
if (storage === void 0) return void 0;
|
|
297
|
+
return storage.kind === "native" ? storage.type : mysqlStorageTypes[storage.type];
|
|
298
|
+
}
|
|
299
|
+
function isMysqlIntegerDeclaration(declaration) {
|
|
300
|
+
const base = declaration.trim().toUpperCase().split(/\s+/u)[0] ?? "";
|
|
301
|
+
return /^(?:TINYINT|SMALLINT|MEDIUMINT|INT|INTEGER|BIGINT)(?:\(\d+\))?$/u.test(base);
|
|
302
|
+
}
|
|
303
|
+
function hasMysqlAutoIncrementKey(columnId, metadata) {
|
|
304
|
+
for (const constraint of Object.values(metadata.constraints)) if ((constraint.kind === "primary-key" || constraint.kind === "unique" || constraint.kind === "unique-constraint") && isColumnReference(constraint.columns[0]) && constraint.columns[0].fieldName === columnId) return true;
|
|
305
|
+
for (const index of Object.values(metadata.indexes)) {
|
|
306
|
+
const first = index.terms[0];
|
|
307
|
+
const expression = isOrderTerm(first) ? first.expression : first;
|
|
308
|
+
if (isColumnReference(expression) && expression.fieldName === columnId) return true;
|
|
309
|
+
}
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
function validateMysqlName(name, path, kind, diagnostics) {
|
|
313
|
+
if (!isValidSchemaObjectName(name) || name.length === 0) diagnostics.push({
|
|
314
|
+
code: "invalid-schema",
|
|
315
|
+
message: `${kind} name "${name}" is not a valid unqualified identifier`,
|
|
316
|
+
path
|
|
317
|
+
});
|
|
318
|
+
if ([...name].length > 64) diagnostics.push({
|
|
319
|
+
code: "unsupported-dialect-option",
|
|
320
|
+
message: `${kind} name exceeds MySQL's 64-character identifier limit`,
|
|
321
|
+
path
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
function addScopedName(names, name, path, kind, diagnostics) {
|
|
325
|
+
const previousPath = names.get(name);
|
|
326
|
+
if (previousPath !== void 0) {
|
|
327
|
+
diagnostics.push({
|
|
328
|
+
code: "invalid-schema",
|
|
329
|
+
message: `MySQL ${kind} name "${name}" collides with another ${kind}`,
|
|
330
|
+
path,
|
|
331
|
+
relatedPaths: [previousPath]
|
|
332
|
+
});
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
names.set(name, path);
|
|
336
|
+
}
|
|
337
|
+
function appendMetadataDiagnostics(diagnostics, issues) {
|
|
338
|
+
for (const issue of issues) {
|
|
339
|
+
if (issue.code === "dialect-mismatch") continue;
|
|
340
|
+
diagnostics.push({
|
|
341
|
+
code: issue.code === "unsupported-dialect-option" ? "unsupported-dialect-option" : "invalid-schema",
|
|
342
|
+
message: issue.message,
|
|
343
|
+
path: issue.path,
|
|
344
|
+
...issue.relatedPaths === void 0 ? {} : { relatedPaths: issue.relatedPaths }
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function isOrderTerm(value) {
|
|
349
|
+
return typeof value === "object" && value !== null && "orderKind" in value && value.orderKind === "term" && "expression" in value;
|
|
350
|
+
}
|
|
351
|
+
function compareIds(left, right) {
|
|
352
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
353
|
+
}
|
|
354
|
+
//#endregion
|
|
355
|
+
export { tryCreateMysqlSchemaSnapshot as a, mysqlSnapshotDialect as i, mysqlSchemaDialect as n, mysqlSnapshotAdapter as r, createMysqlSchemaSnapshot as t };
|
package/dist/mysql.d.mts
ADDED
package/dist/mysql.mjs
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { o as createDialect, t as mysqlJson } from "./json-CUZlv4HT.mjs";
|
|
2
|
+
import { n as queryValidationError } from "./errors-Dxv73YJu.mjs";
|
|
3
|
+
import { t as mysqlExplain } from "./explain-CkIK13L_.mjs";
|
|
4
|
+
//#region src/dialects/mysql.ts
|
|
5
|
+
const mysqlRowLocking = { render(context, mode, wait) {
|
|
6
|
+
if (mode !== "update" && mode !== "share") throw queryValidationError({
|
|
7
|
+
code: "invalid-row-lock",
|
|
8
|
+
context: "dialect.mysql.row-lock",
|
|
9
|
+
path: ["rowLock", "mode"],
|
|
10
|
+
message: `MySQL does not support the "${mode}" row-lock mode`,
|
|
11
|
+
hint: "Use update or share with the MySQL dialect."
|
|
12
|
+
});
|
|
13
|
+
context.append(mode === "update" ? "FOR UPDATE" : "FOR SHARE");
|
|
14
|
+
if (wait === "nowait") context.append(" NOWAIT");
|
|
15
|
+
if (wait === "skip-locked") context.append(" SKIP LOCKED");
|
|
16
|
+
} };
|
|
17
|
+
function renderMySqlPagination(context, parts) {
|
|
18
|
+
const fetch = parts.find((part) => part.kind === "fetch");
|
|
19
|
+
const offset = parts.find((part) => part.kind === "offset");
|
|
20
|
+
context.append("LIMIT ");
|
|
21
|
+
if (fetch) context.parameter(fetch.rows);
|
|
22
|
+
else context.append("18446744073709551615");
|
|
23
|
+
if (offset) {
|
|
24
|
+
context.append(" OFFSET ");
|
|
25
|
+
context.parameter(offset.rows);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function mysqlDialect() {
|
|
29
|
+
return createDialect({
|
|
30
|
+
name: "mysql",
|
|
31
|
+
quoteIdentifier: (identifier) => `\`${identifier.replaceAll("`", "``")}\``,
|
|
32
|
+
placeholder: () => "?",
|
|
33
|
+
pagination: { render: renderMySqlPagination },
|
|
34
|
+
rowLocking: mysqlRowLocking,
|
|
35
|
+
json: mysqlJson,
|
|
36
|
+
capabilities: ["row-locking"],
|
|
37
|
+
castTypes: {
|
|
38
|
+
integer: "SIGNED",
|
|
39
|
+
text: "CHAR",
|
|
40
|
+
boolean: "UNSIGNED",
|
|
41
|
+
timestamp: "DATETIME",
|
|
42
|
+
uuid: "CHAR",
|
|
43
|
+
bigint: "SIGNED",
|
|
44
|
+
binary: "BINARY"
|
|
45
|
+
},
|
|
46
|
+
explain: mysqlExplain
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
export { mysqlDialect };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/core/naming.ts
|
|
2
|
+
/** Convert an application-facing field name to a SQL-facing identifier. */
|
|
3
|
+
function snakeCaseIdentifier(name) {
|
|
4
|
+
return name.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[-\s]+/g, "_").toLowerCase();
|
|
5
|
+
}
|
|
6
|
+
function resolveSqlNames(fields) {
|
|
7
|
+
const sqlNames = {};
|
|
8
|
+
const fieldsBySqlName = /* @__PURE__ */ new Map();
|
|
9
|
+
for (const field of fields) {
|
|
10
|
+
const sqlName = field.sqlName ?? snakeCaseIdentifier(field.fieldName);
|
|
11
|
+
if (sqlName.length === 0) throw new Error(`SQL name for field "${field.fieldName}" cannot be empty`);
|
|
12
|
+
const existingField = fieldsBySqlName.get(sqlName);
|
|
13
|
+
if (existingField) throw new Error(`Fields "${existingField}" and "${field.fieldName}" both resolve to SQL name "${sqlName}"`);
|
|
14
|
+
fieldsBySqlName.set(sqlName, field.fieldName);
|
|
15
|
+
sqlNames[field.fieldName] = sqlName;
|
|
16
|
+
}
|
|
17
|
+
return Object.freeze(sqlNames);
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { snakeCaseIdentifier as n, resolveSqlNames as t };
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { a as assertDialectCapability } from "./json-CUZlv4HT.mjs";
|
|
2
|
+
import { n as queryValidationError } from "./errors-Dxv73YJu.mjs";
|
|
3
|
+
import { i as identifier, o as isExpression, p as fragment, t as createColumnReference } from "./column-CXMxx8Hq.mjs";
|
|
4
|
+
import { t as omit } from "./omit-OxV58AwX.mjs";
|
|
5
|
+
import { r as exposeColumns, t as createSource } from "./source-DUoJVXmL.mjs";
|
|
6
|
+
//#region src/query/mutation/on-conflict.ts
|
|
7
|
+
/** Columns from the proposed INSERT row, available inside DO UPDATE. */
|
|
8
|
+
function excluded(table) {
|
|
9
|
+
const reference = fragment((context) => {
|
|
10
|
+
assertDialectCapability(context.dialect, "on-conflict");
|
|
11
|
+
context.append("excluded");
|
|
12
|
+
});
|
|
13
|
+
const source = createSource("excluded", (context) => context.render(reference), reference);
|
|
14
|
+
const columns = Object.fromEntries(Object.keys(table.definitions).map((fieldName) => [fieldName, createColumnReference(table.sqlNames[fieldName] ?? fieldName, reference, fieldName)]));
|
|
15
|
+
Object.assign(source, { columns });
|
|
16
|
+
exposeColumns(source, columns);
|
|
17
|
+
return source;
|
|
18
|
+
}
|
|
19
|
+
function doNothing() {
|
|
20
|
+
return Object.freeze({ actionKind: "do-nothing" });
|
|
21
|
+
}
|
|
22
|
+
function doUpdate(assignments, whereClause) {
|
|
23
|
+
return Object.freeze({
|
|
24
|
+
actionKind: "do-update",
|
|
25
|
+
assignments,
|
|
26
|
+
where: whereClause
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function onConflict(...args) {
|
|
30
|
+
const table = args.length === 1 ? void 0 : args[0];
|
|
31
|
+
const target = args.length === 1 ? void 0 : args[1];
|
|
32
|
+
const action = args.length === 1 ? args[0] : args[2];
|
|
33
|
+
if (table !== void 0 && target !== void 0) validateConflictTarget(table, target);
|
|
34
|
+
validateConflictAction(table, action);
|
|
35
|
+
return Object.freeze({
|
|
36
|
+
clauseKind: "on-conflict",
|
|
37
|
+
target,
|
|
38
|
+
action,
|
|
39
|
+
render(context) {
|
|
40
|
+
assertDialectCapability(context.dialect, "on-conflict");
|
|
41
|
+
context.append("ON CONFLICT");
|
|
42
|
+
if (target !== void 0) {
|
|
43
|
+
context.append(" (");
|
|
44
|
+
target.columns.forEach((column, index) => {
|
|
45
|
+
if (index > 0) context.append(", ");
|
|
46
|
+
context.render(identifier(table?.sqlNames[column.fieldName] ?? column.columnName));
|
|
47
|
+
});
|
|
48
|
+
context.append(")");
|
|
49
|
+
}
|
|
50
|
+
if (action.actionKind === "do-nothing") {
|
|
51
|
+
context.append(" DO NOTHING");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
context.append(" DO UPDATE SET ");
|
|
55
|
+
Object.entries(action.assignments).filter(([, value]) => value !== omit).forEach(([columnName, value], index) => {
|
|
56
|
+
if (index > 0) context.append(", ");
|
|
57
|
+
context.render(identifier(table?.sqlNames[columnName] ?? columnName));
|
|
58
|
+
context.append(" = ");
|
|
59
|
+
if (isExpression(value)) context.render(value);
|
|
60
|
+
else context.parameter(value);
|
|
61
|
+
});
|
|
62
|
+
if (action.where !== void 0) {
|
|
63
|
+
context.append(" ");
|
|
64
|
+
context.render(action.where);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function validateConflictTarget(table, target) {
|
|
70
|
+
if (target.kind !== "primary-key" && target.kind !== "unique" || target.columns.length === 0) throw queryValidationError({
|
|
71
|
+
code: "invalid-mutation",
|
|
72
|
+
context: "upsert.conflict.target",
|
|
73
|
+
path: ["onConflict", "target"],
|
|
74
|
+
message: "ON CONFLICT requires a non-empty primary or unique key target",
|
|
75
|
+
hint: "Use a declared primaryKey() or unique() constraint from the target table."
|
|
76
|
+
});
|
|
77
|
+
const tableColumns = new Set(Object.values(table.columns));
|
|
78
|
+
if (target.columns.some((column) => !tableColumns.has(column))) throw queryValidationError({
|
|
79
|
+
code: "invalid-mutation",
|
|
80
|
+
context: "upsert.conflict.target",
|
|
81
|
+
path: [
|
|
82
|
+
"onConflict",
|
|
83
|
+
"target",
|
|
84
|
+
"columns"
|
|
85
|
+
],
|
|
86
|
+
message: "ON CONFLICT target columns must belong to the target table",
|
|
87
|
+
hint: "Use the key constraint declared on the INSERT target table."
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function validateConflictAction(table, action) {
|
|
91
|
+
if (action.actionKind === "do-nothing") {
|
|
92
|
+
if (table === void 0) return;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (table === void 0) throw queryValidationError({
|
|
96
|
+
code: "invalid-mutation",
|
|
97
|
+
context: "upsert.conflict.action",
|
|
98
|
+
path: ["onConflict", "action"],
|
|
99
|
+
message: "DO UPDATE requires a target table and conflict target",
|
|
100
|
+
hint: "Pass the target table and a declared key to onConflict()."
|
|
101
|
+
});
|
|
102
|
+
const entries = Object.entries(action.assignments).filter(([, value]) => value !== omit);
|
|
103
|
+
if (entries.length === 0) throw queryValidationError({
|
|
104
|
+
code: "invalid-update",
|
|
105
|
+
context: "upsert.update.assignments",
|
|
106
|
+
path: [
|
|
107
|
+
"onConflict",
|
|
108
|
+
"action",
|
|
109
|
+
"assignments"
|
|
110
|
+
],
|
|
111
|
+
message: "DO UPDATE requires at least one assignment",
|
|
112
|
+
hint: "Provide at least one writable column, or use doNothing()."
|
|
113
|
+
});
|
|
114
|
+
for (const [columnName] of entries) {
|
|
115
|
+
const definition = table.definitions[columnName];
|
|
116
|
+
if (!definition) throw queryValidationError({
|
|
117
|
+
code: "invalid-update",
|
|
118
|
+
context: "upsert.update.assignments",
|
|
119
|
+
path: [
|
|
120
|
+
"onConflict",
|
|
121
|
+
"action",
|
|
122
|
+
"assignments",
|
|
123
|
+
columnName
|
|
124
|
+
],
|
|
125
|
+
message: `Unknown update column "${columnName}"`,
|
|
126
|
+
hint: "Use only columns declared by the INSERT target table."
|
|
127
|
+
});
|
|
128
|
+
if (definition.generated) throw queryValidationError({
|
|
129
|
+
code: "invalid-update",
|
|
130
|
+
context: "upsert.update.assignments",
|
|
131
|
+
path: [
|
|
132
|
+
"onConflict",
|
|
133
|
+
"action",
|
|
134
|
+
"assignments",
|
|
135
|
+
columnName
|
|
136
|
+
],
|
|
137
|
+
message: `Generated column "${columnName}" cannot be updated`,
|
|
138
|
+
hint: "Remove the generated column from the DO UPDATE assignments."
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//#endregion
|
|
143
|
+
export { onConflict as i, doUpdate as n, excluded as r, doNothing as t };
|