flint-orm 0.7.2 → 0.8.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/LICENSE +21 -0
- package/README.md +1 -1
- package/dist/entries/table.d.ts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/migration/types.d.ts +1 -0
- package/dist/schema/table.d.ts +26 -2
- package/dist/src/cli.js +43 -14
- package/dist/src/entries/better-sqlite3.js +13 -9
- package/dist/src/entries/bun-sqlite.js +13 -9
- package/dist/src/entries/expressions.js +13 -9
- package/dist/src/entries/libsql-web.js +13 -9
- package/dist/src/entries/libsql.js +13 -9
- package/dist/src/entries/table.js +41 -1
- package/dist/src/entries/turso-sync.js +13 -9
- package/dist/src/entries/turso.js +13 -9
- package/dist/src/index.js +60 -14
- package/dist/src/migration/index.js +30 -5
- package/package.json +2 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 KavenLabs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
package/dist/entries/table.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { text, integer, boolean, json, date, real } from '../schema/columns';
|
|
2
2
|
export type { ColumnDef, IntegerColumnDef, DateColumnDef, DateColumnDefWithDefault, ForeignKeyAction } from '../schema/columns';
|
|
3
|
-
export { table, index, snakeCase } from '../schema/table';
|
|
4
|
-
export type { InferRow, InsertRow, TableDef, AnyTable, IndexDef, IndexBuilder } from '../schema/table';
|
|
3
|
+
export { table, index, primaryKey, snakeCase } from '../schema/table';
|
|
4
|
+
export type { InferRow, InsertRow, TableDef, AnyTable, IndexDef, IndexBuilder, PrimaryKeyBuilder, PrimaryKeyDef } from '../schema/table';
|
package/dist/index.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ export type { SQLExpression, Executable, SelectBuilder, InsertStage1, UpdateStag
|
|
|
3
3
|
export type { Executor } from './executor';
|
|
4
4
|
export { text, integer, boolean, json, date, real } from './schema/columns';
|
|
5
5
|
export type { ColumnDef, IntegerColumnDef, DateColumnDef, DateColumnDefWithDefault } from './schema/columns';
|
|
6
|
-
export { table, index, snakeCase } from './schema/table';
|
|
7
|
-
export type { InferRow, InsertRow, TableDef, AnyTable, IndexDef, IndexBuilder } from './schema/table';
|
|
6
|
+
export { table, index, primaryKey, snakeCase } from './schema/table';
|
|
7
|
+
export type { InferRow, InsertRow, TableDef, AnyTable, IndexDef, IndexBuilder, PrimaryKeyBuilder, PrimaryKeyDef } from './schema/table';
|
|
8
8
|
export { eq, and, or, gt, gte, lt, lte, neq, isIn, isNotIn, isNull, isNotNull, like, glob, between } from './query/conditions';
|
|
9
9
|
export type { Condition } from './query/conditions';
|
|
10
10
|
export { count, countColumn, sum, avg, min, max } from './query/aggregates';
|
package/dist/schema/table.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export type TableDef<T> = T & {
|
|
|
5
5
|
readonly name: string;
|
|
6
6
|
};
|
|
7
7
|
};
|
|
8
|
-
export declare function table<T extends Record<string, ColumnDef<any, any>>>(name: string, columns: T, indexFn?: (t: TableDef<T>) => IndexBuilder[]): TableDef<T>;
|
|
8
|
+
export declare function table<T extends Record<string, ColumnDef<any, any>>>(name: string, columns: T, indexFn?: (t: TableDef<T>) => (IndexBuilder | PrimaryKeyBuilder)[]): TableDef<T>;
|
|
9
9
|
/**
|
|
10
10
|
* Derive the row shape from a table's column definitions.
|
|
11
11
|
* DateColumnDef → Date | null (nullable unless defaultNow() was called)
|
|
@@ -37,7 +37,7 @@ export type InsertRow<T extends TableDef<any>> = {
|
|
|
37
37
|
* firstName: text(),
|
|
38
38
|
* });
|
|
39
39
|
*/
|
|
40
|
-
export declare function snakeCaseTable<T extends Record<string, ColumnDef<any, any>>>(tableName: string, columns: T, indexFn?: (t: TableDef<T>) => IndexBuilder[]): TableDef<T>;
|
|
40
|
+
export declare function snakeCaseTable<T extends Record<string, ColumnDef<any, any>>>(tableName: string, columns: T, indexFn?: (t: TableDef<T>) => (IndexBuilder | PrimaryKeyBuilder)[]): TableDef<T>;
|
|
41
41
|
/** Provides `snakeCase.table()` for auto snake_case column naming. */
|
|
42
42
|
export declare const snakeCase: {
|
|
43
43
|
table: typeof snakeCaseTable;
|
|
@@ -55,6 +55,30 @@ export interface IndexBuilder {
|
|
|
55
55
|
/** Mark the index as unique. */
|
|
56
56
|
unique(): IndexBuilder;
|
|
57
57
|
}
|
|
58
|
+
/** A composite primary key definition — used internally by the migration system. */
|
|
59
|
+
export interface PrimaryKeyDef {
|
|
60
|
+
columns: string[];
|
|
61
|
+
}
|
|
62
|
+
/** Public builder returned by `primaryKey()` — chain `.on(columns)`. */
|
|
63
|
+
export interface PrimaryKeyBuilder {
|
|
64
|
+
/** Add one or more columns to the composite primary key. */
|
|
65
|
+
on(...columns: ColumnDef<any, any>[]): PrimaryKeyBuilder;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Create a composite primary key definition using a chainable API.
|
|
69
|
+
*
|
|
70
|
+
* @param name - Optional name (reserved for future use, currently unused in SQL)
|
|
71
|
+
* @returns A PrimaryKeyBuilder — chain `.on(columns)`
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* const userRoles = table("user_roles", {
|
|
75
|
+
* userId: text("user_id"),
|
|
76
|
+
* roleId: text("role_id"),
|
|
77
|
+
* }, (t) => [
|
|
78
|
+
* primaryKey().on(t.userId, t.roleId),
|
|
79
|
+
* ]);
|
|
80
|
+
*/
|
|
81
|
+
export declare function primaryKey(): PrimaryKeyBuilderInternal;
|
|
58
82
|
/**
|
|
59
83
|
* Create an index definition using a chainable API.
|
|
60
84
|
*
|
package/dist/src/cli.js
CHANGED
|
@@ -326,6 +326,11 @@ function diffTable(tableName, prev, curr) {
|
|
|
326
326
|
if (unsafe) {
|
|
327
327
|
return [rebuildTable(tableName, prev, curr)];
|
|
328
328
|
}
|
|
329
|
+
const prevPK = prev.primaryKeyColumns ?? [];
|
|
330
|
+
const currPK = curr.primaryKeyColumns ?? [];
|
|
331
|
+
if (JSON.stringify(prevPK) !== JSON.stringify(currPK)) {
|
|
332
|
+
return [rebuildTable(tableName, prev, curr)];
|
|
333
|
+
}
|
|
329
334
|
const ops = [...columnOps];
|
|
330
335
|
const prevIndexes = new Map(prev.indexes.map((i2) => [i2.name, i2]));
|
|
331
336
|
const currIndexes = new Map(curr.indexes.map((i2) => [i2.name, i2]));
|
|
@@ -572,7 +577,17 @@ function serializeTable(table) {
|
|
|
572
577
|
});
|
|
573
578
|
}
|
|
574
579
|
}
|
|
575
|
-
|
|
580
|
+
let primaryKeyColumns;
|
|
581
|
+
if (tableObj.__primaryKey) {
|
|
582
|
+
const pkDef = tableObj.__primaryKey;
|
|
583
|
+
primaryKeyColumns = pkDef.columns;
|
|
584
|
+
for (const col of columns) {
|
|
585
|
+
if (col.isPrimaryKey) {
|
|
586
|
+
throw new Error(`Column "${col.name}" has primaryKey() but table "${tableName}" also defines a composite primaryKey(). Use one or the other.`);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return { name: tableName, columns, indexes, primaryKeyColumns };
|
|
576
591
|
}
|
|
577
592
|
function serializeSchema(tables) {
|
|
578
593
|
const tableMap = {};
|
|
@@ -595,9 +610,9 @@ __export(exports_sql, {
|
|
|
595
610
|
function sqlType(col) {
|
|
596
611
|
return col.sqlType.toUpperCase();
|
|
597
612
|
}
|
|
598
|
-
function columnToDDL(col) {
|
|
613
|
+
function columnToDDL(col, isCompositePK = false) {
|
|
599
614
|
const parts = [col.name, sqlType(col)];
|
|
600
|
-
if (col.isPrimaryKey)
|
|
615
|
+
if (col.isPrimaryKey && !isCompositePK)
|
|
601
616
|
parts.push("PRIMARY KEY");
|
|
602
617
|
if (col.isAutoIncrement === true)
|
|
603
618
|
parts.push("AUTOINCREMENT");
|
|
@@ -644,7 +659,12 @@ function rebuildTableToSQL(op) {
|
|
|
644
659
|
const tempName = `_flint_rebuild_${tableName}`;
|
|
645
660
|
const stmts = [];
|
|
646
661
|
stmts.push("PRAGMA defer_foreign_keys = 1");
|
|
647
|
-
const
|
|
662
|
+
const isComposite = newTable.primaryKeyColumns && newTable.primaryKeyColumns.length > 0;
|
|
663
|
+
const colDefs = newTable.columns.map((c3) => columnToDDL(c3, isComposite));
|
|
664
|
+
if (isComposite) {
|
|
665
|
+
colDefs.push(`PRIMARY KEY(${newTable.primaryKeyColumns.join(", ")})`);
|
|
666
|
+
}
|
|
667
|
+
const cols = colDefs.join(`,
|
|
648
668
|
`);
|
|
649
669
|
stmts.push(`CREATE TABLE ${tempName} (
|
|
650
670
|
${cols}
|
|
@@ -671,7 +691,12 @@ function rebuildTableToSQL(op) {
|
|
|
671
691
|
function operationToSQL(op) {
|
|
672
692
|
switch (op.type) {
|
|
673
693
|
case "addTable": {
|
|
674
|
-
const
|
|
694
|
+
const isComposite = op.table.primaryKeyColumns && op.table.primaryKeyColumns.length > 0;
|
|
695
|
+
const colDefs = op.table.columns.map((c3) => columnToDDL(c3, isComposite));
|
|
696
|
+
if (isComposite) {
|
|
697
|
+
colDefs.push(`PRIMARY KEY(${op.table.primaryKeyColumns.join(", ")})`);
|
|
698
|
+
}
|
|
699
|
+
const cols = colDefs.join(`,
|
|
675
700
|
`);
|
|
676
701
|
const stmts = [`CREATE TABLE ${op.table.name} (
|
|
677
702
|
${cols}
|
|
@@ -1315,12 +1340,16 @@ function decodeSelectedRow(raw, tbl, keys) {
|
|
|
1315
1340
|
}
|
|
1316
1341
|
return out;
|
|
1317
1342
|
}
|
|
1318
|
-
function
|
|
1343
|
+
function findPKKeys(tbl) {
|
|
1344
|
+
const keys = [];
|
|
1319
1345
|
for (const [key, col] of columnEntries(tbl)) {
|
|
1320
1346
|
if (col.__internal.isPrimaryKey)
|
|
1321
|
-
|
|
1347
|
+
keys.push(key);
|
|
1348
|
+
}
|
|
1349
|
+
if (keys.length === 0) {
|
|
1350
|
+
throw new FlintValidationError("Table has no primary key column");
|
|
1322
1351
|
}
|
|
1323
|
-
|
|
1352
|
+
return keys;
|
|
1324
1353
|
}
|
|
1325
1354
|
function resolveForeignKeyCondition(parent, parentName, child, childName) {
|
|
1326
1355
|
for (const [, col] of columnEntries(child)) {
|
|
@@ -1758,8 +1787,8 @@ class JoinBuilderImpl {
|
|
|
1758
1787
|
}
|
|
1759
1788
|
#decodeJoinRows(rows) {
|
|
1760
1789
|
const parentEntries = columnEntries(this.#parent);
|
|
1761
|
-
const
|
|
1762
|
-
const
|
|
1790
|
+
const pkKeys = findPKKeys(this.#parent);
|
|
1791
|
+
const pkColNames = pkKeys.map((k) => getCol(this.#parent, k).name);
|
|
1763
1792
|
const childEntryMaps = [];
|
|
1764
1793
|
for (const j of this.#joins) {
|
|
1765
1794
|
childEntryMaps.push({
|
|
@@ -1770,7 +1799,7 @@ class JoinBuilderImpl {
|
|
|
1770
1799
|
}
|
|
1771
1800
|
const grouped = new Map;
|
|
1772
1801
|
for (const row of rows) {
|
|
1773
|
-
const pk = row[
|
|
1802
|
+
const pk = pkColNames.length === 1 ? row[pkColNames[0]] : pkColNames.map((name) => String(row[name])).join("||");
|
|
1774
1803
|
if (!grouped.has(pk)) {
|
|
1775
1804
|
const parentRow = {};
|
|
1776
1805
|
for (const [key, col] of parentEntries) {
|
|
@@ -1888,8 +1917,8 @@ class SingleJoinBuilderImpl {
|
|
|
1888
1917
|
}
|
|
1889
1918
|
#decodeJoinRow(rows) {
|
|
1890
1919
|
const parentEntries = columnEntries(this.#parent);
|
|
1891
|
-
const
|
|
1892
|
-
const
|
|
1920
|
+
const pkKeys = findPKKeys(this.#parent);
|
|
1921
|
+
const pkColNames = pkKeys.map((k) => getCol(this.#parent, k).name);
|
|
1893
1922
|
const childEntryMaps = [];
|
|
1894
1923
|
for (const j of this.#joins) {
|
|
1895
1924
|
childEntryMaps.push({
|
|
@@ -1900,7 +1929,7 @@ class SingleJoinBuilderImpl {
|
|
|
1900
1929
|
}
|
|
1901
1930
|
const grouped = new Map;
|
|
1902
1931
|
for (const r2 of rows) {
|
|
1903
|
-
const pk = r2[
|
|
1932
|
+
const pk = pkColNames.length === 1 ? r2[pkColNames[0]] : pkColNames.map((name) => String(r2[name])).join("||");
|
|
1904
1933
|
if (!grouped.has(pk)) {
|
|
1905
1934
|
const parentRow = {};
|
|
1906
1935
|
for (const [key, col] of parentEntries) {
|
|
@@ -976,12 +976,16 @@ function decodeSelectedRow(raw, tbl, keys) {
|
|
|
976
976
|
}
|
|
977
977
|
return out;
|
|
978
978
|
}
|
|
979
|
-
function
|
|
979
|
+
function findPKKeys(tbl) {
|
|
980
|
+
const keys = [];
|
|
980
981
|
for (const [key, col] of columnEntries(tbl)) {
|
|
981
982
|
if (col.__internal.isPrimaryKey)
|
|
982
|
-
|
|
983
|
+
keys.push(key);
|
|
983
984
|
}
|
|
984
|
-
|
|
985
|
+
if (keys.length === 0) {
|
|
986
|
+
throw new FlintValidationError("Table has no primary key column");
|
|
987
|
+
}
|
|
988
|
+
return keys;
|
|
985
989
|
}
|
|
986
990
|
function resolveForeignKeyCondition(parent, parentName, child, childName) {
|
|
987
991
|
for (const [, col] of columnEntries(child)) {
|
|
@@ -1419,8 +1423,8 @@ class JoinBuilderImpl {
|
|
|
1419
1423
|
}
|
|
1420
1424
|
#decodeJoinRows(rows) {
|
|
1421
1425
|
const parentEntries = columnEntries(this.#parent);
|
|
1422
|
-
const
|
|
1423
|
-
const
|
|
1426
|
+
const pkKeys = findPKKeys(this.#parent);
|
|
1427
|
+
const pkColNames = pkKeys.map((k) => getCol(this.#parent, k).name);
|
|
1424
1428
|
const childEntryMaps = [];
|
|
1425
1429
|
for (const j of this.#joins) {
|
|
1426
1430
|
childEntryMaps.push({
|
|
@@ -1431,7 +1435,7 @@ class JoinBuilderImpl {
|
|
|
1431
1435
|
}
|
|
1432
1436
|
const grouped = new Map;
|
|
1433
1437
|
for (const row of rows) {
|
|
1434
|
-
const pk = row[
|
|
1438
|
+
const pk = pkColNames.length === 1 ? row[pkColNames[0]] : pkColNames.map((name) => String(row[name])).join("||");
|
|
1435
1439
|
if (!grouped.has(pk)) {
|
|
1436
1440
|
const parentRow = {};
|
|
1437
1441
|
for (const [key, col] of parentEntries) {
|
|
@@ -1549,8 +1553,8 @@ class SingleJoinBuilderImpl {
|
|
|
1549
1553
|
}
|
|
1550
1554
|
#decodeJoinRow(rows) {
|
|
1551
1555
|
const parentEntries = columnEntries(this.#parent);
|
|
1552
|
-
const
|
|
1553
|
-
const
|
|
1556
|
+
const pkKeys = findPKKeys(this.#parent);
|
|
1557
|
+
const pkColNames = pkKeys.map((k) => getCol(this.#parent, k).name);
|
|
1554
1558
|
const childEntryMaps = [];
|
|
1555
1559
|
for (const j of this.#joins) {
|
|
1556
1560
|
childEntryMaps.push({
|
|
@@ -1561,7 +1565,7 @@ class SingleJoinBuilderImpl {
|
|
|
1561
1565
|
}
|
|
1562
1566
|
const grouped = new Map;
|
|
1563
1567
|
for (const r of rows) {
|
|
1564
|
-
const pk = r[
|
|
1568
|
+
const pk = pkColNames.length === 1 ? r[pkColNames[0]] : pkColNames.map((name) => String(r[name])).join("||");
|
|
1565
1569
|
if (!grouped.has(pk)) {
|
|
1566
1570
|
const parentRow = {};
|
|
1567
1571
|
for (const [key, col] of parentEntries) {
|
|
@@ -211,12 +211,16 @@ function decodeSelectedRow(raw, tbl, keys) {
|
|
|
211
211
|
}
|
|
212
212
|
return out;
|
|
213
213
|
}
|
|
214
|
-
function
|
|
214
|
+
function findPKKeys(tbl) {
|
|
215
|
+
const keys = [];
|
|
215
216
|
for (const [key, col] of columnEntries(tbl)) {
|
|
216
217
|
if (col.__internal.isPrimaryKey)
|
|
217
|
-
|
|
218
|
+
keys.push(key);
|
|
218
219
|
}
|
|
219
|
-
|
|
220
|
+
if (keys.length === 0) {
|
|
221
|
+
throw new FlintValidationError("Table has no primary key column");
|
|
222
|
+
}
|
|
223
|
+
return keys;
|
|
220
224
|
}
|
|
221
225
|
function resolveForeignKeyCondition(parent, parentName, child, childName) {
|
|
222
226
|
for (const [, col] of columnEntries(child)) {
|
|
@@ -654,8 +658,8 @@ class JoinBuilderImpl {
|
|
|
654
658
|
}
|
|
655
659
|
#decodeJoinRows(rows) {
|
|
656
660
|
const parentEntries = columnEntries(this.#parent);
|
|
657
|
-
const
|
|
658
|
-
const
|
|
661
|
+
const pkKeys = findPKKeys(this.#parent);
|
|
662
|
+
const pkColNames = pkKeys.map((k) => getCol(this.#parent, k).name);
|
|
659
663
|
const childEntryMaps = [];
|
|
660
664
|
for (const j of this.#joins) {
|
|
661
665
|
childEntryMaps.push({
|
|
@@ -666,7 +670,7 @@ class JoinBuilderImpl {
|
|
|
666
670
|
}
|
|
667
671
|
const grouped = new Map;
|
|
668
672
|
for (const row of rows) {
|
|
669
|
-
const pk = row[
|
|
673
|
+
const pk = pkColNames.length === 1 ? row[pkColNames[0]] : pkColNames.map((name) => String(row[name])).join("||");
|
|
670
674
|
if (!grouped.has(pk)) {
|
|
671
675
|
const parentRow = {};
|
|
672
676
|
for (const [key, col] of parentEntries) {
|
|
@@ -784,8 +788,8 @@ class SingleJoinBuilderImpl {
|
|
|
784
788
|
}
|
|
785
789
|
#decodeJoinRow(rows) {
|
|
786
790
|
const parentEntries = columnEntries(this.#parent);
|
|
787
|
-
const
|
|
788
|
-
const
|
|
791
|
+
const pkKeys = findPKKeys(this.#parent);
|
|
792
|
+
const pkColNames = pkKeys.map((k) => getCol(this.#parent, k).name);
|
|
789
793
|
const childEntryMaps = [];
|
|
790
794
|
for (const j of this.#joins) {
|
|
791
795
|
childEntryMaps.push({
|
|
@@ -796,7 +800,7 @@ class SingleJoinBuilderImpl {
|
|
|
796
800
|
}
|
|
797
801
|
const grouped = new Map;
|
|
798
802
|
for (const r of rows) {
|
|
799
|
-
const pk = r[
|
|
803
|
+
const pk = pkColNames.length === 1 ? r[pkColNames[0]] : pkColNames.map((name) => String(r[name])).join("||");
|
|
800
804
|
if (!grouped.has(pk)) {
|
|
801
805
|
const parentRow = {};
|
|
802
806
|
for (const [key, col] of parentEntries) {
|
|
@@ -211,12 +211,16 @@ function decodeSelectedRow(raw, tbl, keys) {
|
|
|
211
211
|
}
|
|
212
212
|
return out;
|
|
213
213
|
}
|
|
214
|
-
function
|
|
214
|
+
function findPKKeys(tbl) {
|
|
215
|
+
const keys = [];
|
|
215
216
|
for (const [key, col] of columnEntries(tbl)) {
|
|
216
217
|
if (col.__internal.isPrimaryKey)
|
|
217
|
-
|
|
218
|
+
keys.push(key);
|
|
218
219
|
}
|
|
219
|
-
|
|
220
|
+
if (keys.length === 0) {
|
|
221
|
+
throw new FlintValidationError("Table has no primary key column");
|
|
222
|
+
}
|
|
223
|
+
return keys;
|
|
220
224
|
}
|
|
221
225
|
function resolveForeignKeyCondition(parent, parentName, child, childName) {
|
|
222
226
|
for (const [, col] of columnEntries(child)) {
|
|
@@ -654,8 +658,8 @@ class JoinBuilderImpl {
|
|
|
654
658
|
}
|
|
655
659
|
#decodeJoinRows(rows) {
|
|
656
660
|
const parentEntries = columnEntries(this.#parent);
|
|
657
|
-
const
|
|
658
|
-
const
|
|
661
|
+
const pkKeys = findPKKeys(this.#parent);
|
|
662
|
+
const pkColNames = pkKeys.map((k) => getCol(this.#parent, k).name);
|
|
659
663
|
const childEntryMaps = [];
|
|
660
664
|
for (const j of this.#joins) {
|
|
661
665
|
childEntryMaps.push({
|
|
@@ -666,7 +670,7 @@ class JoinBuilderImpl {
|
|
|
666
670
|
}
|
|
667
671
|
const grouped = new Map;
|
|
668
672
|
for (const row of rows) {
|
|
669
|
-
const pk = row[
|
|
673
|
+
const pk = pkColNames.length === 1 ? row[pkColNames[0]] : pkColNames.map((name) => String(row[name])).join("||");
|
|
670
674
|
if (!grouped.has(pk)) {
|
|
671
675
|
const parentRow = {};
|
|
672
676
|
for (const [key, col] of parentEntries) {
|
|
@@ -784,8 +788,8 @@ class SingleJoinBuilderImpl {
|
|
|
784
788
|
}
|
|
785
789
|
#decodeJoinRow(rows) {
|
|
786
790
|
const parentEntries = columnEntries(this.#parent);
|
|
787
|
-
const
|
|
788
|
-
const
|
|
791
|
+
const pkKeys = findPKKeys(this.#parent);
|
|
792
|
+
const pkColNames = pkKeys.map((k) => getCol(this.#parent, k).name);
|
|
789
793
|
const childEntryMaps = [];
|
|
790
794
|
for (const j of this.#joins) {
|
|
791
795
|
childEntryMaps.push({
|
|
@@ -796,7 +800,7 @@ class SingleJoinBuilderImpl {
|
|
|
796
800
|
}
|
|
797
801
|
const grouped = new Map;
|
|
798
802
|
for (const r of rows) {
|
|
799
|
-
const pk = r[
|
|
803
|
+
const pk = pkColNames.length === 1 ? r[pkColNames[0]] : pkColNames.map((name) => String(r[name])).join("||");
|
|
800
804
|
if (!grouped.has(pk)) {
|
|
801
805
|
const parentRow = {};
|
|
802
806
|
for (const [key, col] of parentEntries) {
|
|
@@ -4997,12 +4997,16 @@ function decodeSelectedRow(raw, tbl, keys) {
|
|
|
4997
4997
|
}
|
|
4998
4998
|
return out;
|
|
4999
4999
|
}
|
|
5000
|
-
function
|
|
5000
|
+
function findPKKeys(tbl) {
|
|
5001
|
+
const keys = [];
|
|
5001
5002
|
for (const [key, col] of columnEntries(tbl)) {
|
|
5002
5003
|
if (col.__internal.isPrimaryKey)
|
|
5003
|
-
|
|
5004
|
+
keys.push(key);
|
|
5004
5005
|
}
|
|
5005
|
-
|
|
5006
|
+
if (keys.length === 0) {
|
|
5007
|
+
throw new FlintValidationError("Table has no primary key column");
|
|
5008
|
+
}
|
|
5009
|
+
return keys;
|
|
5006
5010
|
}
|
|
5007
5011
|
function resolveForeignKeyCondition(parent, parentName, child, childName) {
|
|
5008
5012
|
for (const [, col] of columnEntries(child)) {
|
|
@@ -5440,8 +5444,8 @@ class JoinBuilderImpl {
|
|
|
5440
5444
|
}
|
|
5441
5445
|
#decodeJoinRows(rows) {
|
|
5442
5446
|
const parentEntries = columnEntries(this.#parent);
|
|
5443
|
-
const
|
|
5444
|
-
const
|
|
5447
|
+
const pkKeys = findPKKeys(this.#parent);
|
|
5448
|
+
const pkColNames = pkKeys.map((k) => getCol(this.#parent, k).name);
|
|
5445
5449
|
const childEntryMaps = [];
|
|
5446
5450
|
for (const j of this.#joins) {
|
|
5447
5451
|
childEntryMaps.push({
|
|
@@ -5452,7 +5456,7 @@ class JoinBuilderImpl {
|
|
|
5452
5456
|
}
|
|
5453
5457
|
const grouped = new Map;
|
|
5454
5458
|
for (const row of rows) {
|
|
5455
|
-
const pk = row[
|
|
5459
|
+
const pk = pkColNames.length === 1 ? row[pkColNames[0]] : pkColNames.map((name) => String(row[name])).join("||");
|
|
5456
5460
|
if (!grouped.has(pk)) {
|
|
5457
5461
|
const parentRow = {};
|
|
5458
5462
|
for (const [key, col] of parentEntries) {
|
|
@@ -5570,8 +5574,8 @@ class SingleJoinBuilderImpl {
|
|
|
5570
5574
|
}
|
|
5571
5575
|
#decodeJoinRow(rows) {
|
|
5572
5576
|
const parentEntries = columnEntries(this.#parent);
|
|
5573
|
-
const
|
|
5574
|
-
const
|
|
5577
|
+
const pkKeys = findPKKeys(this.#parent);
|
|
5578
|
+
const pkColNames = pkKeys.map((k) => getCol(this.#parent, k).name);
|
|
5575
5579
|
const childEntryMaps = [];
|
|
5576
5580
|
for (const j of this.#joins) {
|
|
5577
5581
|
childEntryMaps.push({
|
|
@@ -5582,7 +5586,7 @@ class SingleJoinBuilderImpl {
|
|
|
5582
5586
|
}
|
|
5583
5587
|
const grouped = new Map;
|
|
5584
5588
|
for (const r of rows) {
|
|
5585
|
-
const pk = r[
|
|
5589
|
+
const pk = pkColNames.length === 1 ? r[pkColNames[0]] : pkColNames.map((name) => String(r[name])).join("||");
|
|
5586
5590
|
if (!grouped.has(pk)) {
|
|
5587
5591
|
const parentRow = {};
|
|
5588
5592
|
for (const [key, col] of parentEntries) {
|
|
@@ -6131,12 +6131,16 @@ function decodeSelectedRow(raw, tbl, keys) {
|
|
|
6131
6131
|
}
|
|
6132
6132
|
return out;
|
|
6133
6133
|
}
|
|
6134
|
-
function
|
|
6134
|
+
function findPKKeys(tbl) {
|
|
6135
|
+
const keys = [];
|
|
6135
6136
|
for (const [key, col] of columnEntries(tbl)) {
|
|
6136
6137
|
if (col.__internal.isPrimaryKey)
|
|
6137
|
-
|
|
6138
|
+
keys.push(key);
|
|
6138
6139
|
}
|
|
6139
|
-
|
|
6140
|
+
if (keys.length === 0) {
|
|
6141
|
+
throw new FlintValidationError("Table has no primary key column");
|
|
6142
|
+
}
|
|
6143
|
+
return keys;
|
|
6140
6144
|
}
|
|
6141
6145
|
function resolveForeignKeyCondition(parent, parentName, child, childName) {
|
|
6142
6146
|
for (const [, col] of columnEntries(child)) {
|
|
@@ -6574,8 +6578,8 @@ class JoinBuilderImpl {
|
|
|
6574
6578
|
}
|
|
6575
6579
|
#decodeJoinRows(rows) {
|
|
6576
6580
|
const parentEntries = columnEntries(this.#parent);
|
|
6577
|
-
const
|
|
6578
|
-
const
|
|
6581
|
+
const pkKeys = findPKKeys(this.#parent);
|
|
6582
|
+
const pkColNames = pkKeys.map((k) => getCol(this.#parent, k).name);
|
|
6579
6583
|
const childEntryMaps = [];
|
|
6580
6584
|
for (const j of this.#joins) {
|
|
6581
6585
|
childEntryMaps.push({
|
|
@@ -6586,7 +6590,7 @@ class JoinBuilderImpl {
|
|
|
6586
6590
|
}
|
|
6587
6591
|
const grouped = new Map;
|
|
6588
6592
|
for (const row of rows) {
|
|
6589
|
-
const pk = row[
|
|
6593
|
+
const pk = pkColNames.length === 1 ? row[pkColNames[0]] : pkColNames.map((name) => String(row[name])).join("||");
|
|
6590
6594
|
if (!grouped.has(pk)) {
|
|
6591
6595
|
const parentRow = {};
|
|
6592
6596
|
for (const [key, col] of parentEntries) {
|
|
@@ -6704,8 +6708,8 @@ class SingleJoinBuilderImpl {
|
|
|
6704
6708
|
}
|
|
6705
6709
|
#decodeJoinRow(rows) {
|
|
6706
6710
|
const parentEntries = columnEntries(this.#parent);
|
|
6707
|
-
const
|
|
6708
|
-
const
|
|
6711
|
+
const pkKeys = findPKKeys(this.#parent);
|
|
6712
|
+
const pkColNames = pkKeys.map((k) => getCol(this.#parent, k).name);
|
|
6709
6713
|
const childEntryMaps = [];
|
|
6710
6714
|
for (const j of this.#joins) {
|
|
6711
6715
|
childEntryMaps.push({
|
|
@@ -6716,7 +6720,7 @@ class SingleJoinBuilderImpl {
|
|
|
6716
6720
|
}
|
|
6717
6721
|
const grouped = new Map;
|
|
6718
6722
|
for (const r of rows) {
|
|
6719
|
-
const pk = r[
|
|
6723
|
+
const pk = pkColNames.length === 1 ? r[pkColNames[0]] : pkColNames.map((name) => String(r[name])).join("||");
|
|
6720
6724
|
if (!grouped.has(pk)) {
|
|
6721
6725
|
const parentRow = {};
|
|
6722
6726
|
for (const [key, col] of parentEntries) {
|
|
@@ -291,7 +291,28 @@ function table(name, columns, indexFn) {
|
|
|
291
291
|
const raw = indexFn(result);
|
|
292
292
|
if (raw.length > 0) {
|
|
293
293
|
const tableObj = result;
|
|
294
|
-
|
|
294
|
+
const indexes = [];
|
|
295
|
+
let primaryKeyDef;
|
|
296
|
+
for (const item of raw) {
|
|
297
|
+
if (item && typeof item === "object" && "_type" in item) {
|
|
298
|
+
if (item._type === "primaryKey") {
|
|
299
|
+
primaryKeyDef = item.build();
|
|
300
|
+
} else {
|
|
301
|
+
indexes.push(item.build());
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (primaryKeyDef) {
|
|
306
|
+
for (const [key, col] of Object.entries(columns)) {
|
|
307
|
+
if (col.__internal.isPrimaryKey) {
|
|
308
|
+
throw new Error(`Column "${key}" has primaryKey() but table "${name}" also defines a composite primaryKey(). Use one or the other.`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
tableObj.__primaryKey = primaryKeyDef;
|
|
312
|
+
}
|
|
313
|
+
if (indexes.length > 0) {
|
|
314
|
+
tableObj.__indexes = indexes;
|
|
315
|
+
}
|
|
295
316
|
}
|
|
296
317
|
}
|
|
297
318
|
return result;
|
|
@@ -365,10 +386,28 @@ function snakeCaseTable(tableName, columns, indexFn) {
|
|
|
365
386
|
var snakeCase = {
|
|
366
387
|
table: snakeCaseTable
|
|
367
388
|
};
|
|
389
|
+
function primaryKey() {
|
|
390
|
+
let columns = [];
|
|
391
|
+
const builder = {
|
|
392
|
+
_type: "primaryKey",
|
|
393
|
+
on(...cols) {
|
|
394
|
+
columns = cols;
|
|
395
|
+
return builder;
|
|
396
|
+
},
|
|
397
|
+
build() {
|
|
398
|
+
if (columns.length === 0) {
|
|
399
|
+
throw new Error("primaryKey() has no columns \u2014 call .on() before returning");
|
|
400
|
+
}
|
|
401
|
+
return { columns: columns.map((c) => c.name) };
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
return builder;
|
|
405
|
+
}
|
|
368
406
|
function index(name) {
|
|
369
407
|
let columns = [];
|
|
370
408
|
let isUnique = false;
|
|
371
409
|
const builder = {
|
|
410
|
+
_type: "index",
|
|
372
411
|
on(...cols) {
|
|
373
412
|
columns = cols;
|
|
374
413
|
return builder;
|
|
@@ -395,6 +434,7 @@ export {
|
|
|
395
434
|
table,
|
|
396
435
|
snakeCase,
|
|
397
436
|
real,
|
|
437
|
+
primaryKey,
|
|
398
438
|
json,
|
|
399
439
|
integer,
|
|
400
440
|
index,
|
|
@@ -2432,12 +2432,16 @@ function decodeSelectedRow(raw, tbl, keys) {
|
|
|
2432
2432
|
}
|
|
2433
2433
|
return out;
|
|
2434
2434
|
}
|
|
2435
|
-
function
|
|
2435
|
+
function findPKKeys(tbl) {
|
|
2436
|
+
const keys = [];
|
|
2436
2437
|
for (const [key, col] of columnEntries(tbl)) {
|
|
2437
2438
|
if (col.__internal.isPrimaryKey)
|
|
2438
|
-
|
|
2439
|
+
keys.push(key);
|
|
2439
2440
|
}
|
|
2440
|
-
|
|
2441
|
+
if (keys.length === 0) {
|
|
2442
|
+
throw new FlintValidationError("Table has no primary key column");
|
|
2443
|
+
}
|
|
2444
|
+
return keys;
|
|
2441
2445
|
}
|
|
2442
2446
|
function resolveForeignKeyCondition(parent, parentName, child, childName) {
|
|
2443
2447
|
for (const [, col] of columnEntries(child)) {
|
|
@@ -2875,8 +2879,8 @@ class JoinBuilderImpl {
|
|
|
2875
2879
|
}
|
|
2876
2880
|
#decodeJoinRows(rows) {
|
|
2877
2881
|
const parentEntries = columnEntries(this.#parent);
|
|
2878
|
-
const
|
|
2879
|
-
const
|
|
2882
|
+
const pkKeys = findPKKeys(this.#parent);
|
|
2883
|
+
const pkColNames = pkKeys.map((k) => getCol(this.#parent, k).name);
|
|
2880
2884
|
const childEntryMaps = [];
|
|
2881
2885
|
for (const j of this.#joins) {
|
|
2882
2886
|
childEntryMaps.push({
|
|
@@ -2887,7 +2891,7 @@ class JoinBuilderImpl {
|
|
|
2887
2891
|
}
|
|
2888
2892
|
const grouped = new Map;
|
|
2889
2893
|
for (const row of rows) {
|
|
2890
|
-
const pk = row[
|
|
2894
|
+
const pk = pkColNames.length === 1 ? row[pkColNames[0]] : pkColNames.map((name) => String(row[name])).join("||");
|
|
2891
2895
|
if (!grouped.has(pk)) {
|
|
2892
2896
|
const parentRow = {};
|
|
2893
2897
|
for (const [key, col] of parentEntries) {
|
|
@@ -3005,8 +3009,8 @@ class SingleJoinBuilderImpl {
|
|
|
3005
3009
|
}
|
|
3006
3010
|
#decodeJoinRow(rows) {
|
|
3007
3011
|
const parentEntries = columnEntries(this.#parent);
|
|
3008
|
-
const
|
|
3009
|
-
const
|
|
3012
|
+
const pkKeys = findPKKeys(this.#parent);
|
|
3013
|
+
const pkColNames = pkKeys.map((k) => getCol(this.#parent, k).name);
|
|
3010
3014
|
const childEntryMaps = [];
|
|
3011
3015
|
for (const j of this.#joins) {
|
|
3012
3016
|
childEntryMaps.push({
|
|
@@ -3017,7 +3021,7 @@ class SingleJoinBuilderImpl {
|
|
|
3017
3021
|
}
|
|
3018
3022
|
const grouped = new Map;
|
|
3019
3023
|
for (const r of rows) {
|
|
3020
|
-
const pk = r[
|
|
3024
|
+
const pk = pkColNames.length === 1 ? r[pkColNames[0]] : pkColNames.map((name) => String(r[name])).join("||");
|
|
3021
3025
|
if (!grouped.has(pk)) {
|
|
3022
3026
|
const parentRow = {};
|
|
3023
3027
|
for (const [key, col] of parentEntries) {
|
|
@@ -1313,12 +1313,16 @@ function decodeSelectedRow(raw, tbl, keys) {
|
|
|
1313
1313
|
}
|
|
1314
1314
|
return out;
|
|
1315
1315
|
}
|
|
1316
|
-
function
|
|
1316
|
+
function findPKKeys(tbl) {
|
|
1317
|
+
const keys = [];
|
|
1317
1318
|
for (const [key, col] of columnEntries(tbl)) {
|
|
1318
1319
|
if (col.__internal.isPrimaryKey)
|
|
1319
|
-
|
|
1320
|
+
keys.push(key);
|
|
1320
1321
|
}
|
|
1321
|
-
|
|
1322
|
+
if (keys.length === 0) {
|
|
1323
|
+
throw new FlintValidationError("Table has no primary key column");
|
|
1324
|
+
}
|
|
1325
|
+
return keys;
|
|
1322
1326
|
}
|
|
1323
1327
|
function resolveForeignKeyCondition(parent, parentName, child, childName) {
|
|
1324
1328
|
for (const [, col] of columnEntries(child)) {
|
|
@@ -1756,8 +1760,8 @@ class JoinBuilderImpl {
|
|
|
1756
1760
|
}
|
|
1757
1761
|
#decodeJoinRows(rows) {
|
|
1758
1762
|
const parentEntries = columnEntries(this.#parent);
|
|
1759
|
-
const
|
|
1760
|
-
const
|
|
1763
|
+
const pkKeys = findPKKeys(this.#parent);
|
|
1764
|
+
const pkColNames = pkKeys.map((k) => getCol(this.#parent, k).name);
|
|
1761
1765
|
const childEntryMaps = [];
|
|
1762
1766
|
for (const j of this.#joins) {
|
|
1763
1767
|
childEntryMaps.push({
|
|
@@ -1768,7 +1772,7 @@ class JoinBuilderImpl {
|
|
|
1768
1772
|
}
|
|
1769
1773
|
const grouped = new Map;
|
|
1770
1774
|
for (const row of rows) {
|
|
1771
|
-
const pk = row[
|
|
1775
|
+
const pk = pkColNames.length === 1 ? row[pkColNames[0]] : pkColNames.map((name) => String(row[name])).join("||");
|
|
1772
1776
|
if (!grouped.has(pk)) {
|
|
1773
1777
|
const parentRow = {};
|
|
1774
1778
|
for (const [key, col] of parentEntries) {
|
|
@@ -1886,8 +1890,8 @@ class SingleJoinBuilderImpl {
|
|
|
1886
1890
|
}
|
|
1887
1891
|
#decodeJoinRow(rows) {
|
|
1888
1892
|
const parentEntries = columnEntries(this.#parent);
|
|
1889
|
-
const
|
|
1890
|
-
const
|
|
1893
|
+
const pkKeys = findPKKeys(this.#parent);
|
|
1894
|
+
const pkColNames = pkKeys.map((k) => getCol(this.#parent, k).name);
|
|
1891
1895
|
const childEntryMaps = [];
|
|
1892
1896
|
for (const j of this.#joins) {
|
|
1893
1897
|
childEntryMaps.push({
|
|
@@ -1898,7 +1902,7 @@ class SingleJoinBuilderImpl {
|
|
|
1898
1902
|
}
|
|
1899
1903
|
const grouped = new Map;
|
|
1900
1904
|
for (const r of rows) {
|
|
1901
|
-
const pk = r[
|
|
1905
|
+
const pk = pkColNames.length === 1 ? r[pkColNames[0]] : pkColNames.map((name) => String(r[name])).join("||");
|
|
1902
1906
|
if (!grouped.has(pk)) {
|
|
1903
1907
|
const parentRow = {};
|
|
1904
1908
|
for (const [key, col] of parentEntries) {
|
package/dist/src/index.js
CHANGED
|
@@ -211,12 +211,16 @@ function decodeSelectedRow(raw, tbl, keys) {
|
|
|
211
211
|
}
|
|
212
212
|
return out;
|
|
213
213
|
}
|
|
214
|
-
function
|
|
214
|
+
function findPKKeys(tbl) {
|
|
215
|
+
const keys = [];
|
|
215
216
|
for (const [key, col] of columnEntries(tbl)) {
|
|
216
217
|
if (col.__internal.isPrimaryKey)
|
|
217
|
-
|
|
218
|
+
keys.push(key);
|
|
218
219
|
}
|
|
219
|
-
|
|
220
|
+
if (keys.length === 0) {
|
|
221
|
+
throw new FlintValidationError("Table has no primary key column");
|
|
222
|
+
}
|
|
223
|
+
return keys;
|
|
220
224
|
}
|
|
221
225
|
function resolveForeignKeyCondition(parent, parentName, child, childName) {
|
|
222
226
|
for (const [, col] of columnEntries(child)) {
|
|
@@ -654,8 +658,8 @@ class JoinBuilderImpl {
|
|
|
654
658
|
}
|
|
655
659
|
#decodeJoinRows(rows) {
|
|
656
660
|
const parentEntries = columnEntries(this.#parent);
|
|
657
|
-
const
|
|
658
|
-
const
|
|
661
|
+
const pkKeys = findPKKeys(this.#parent);
|
|
662
|
+
const pkColNames = pkKeys.map((k) => getCol(this.#parent, k).name);
|
|
659
663
|
const childEntryMaps = [];
|
|
660
664
|
for (const j of this.#joins) {
|
|
661
665
|
childEntryMaps.push({
|
|
@@ -666,7 +670,7 @@ class JoinBuilderImpl {
|
|
|
666
670
|
}
|
|
667
671
|
const grouped = new Map;
|
|
668
672
|
for (const row of rows) {
|
|
669
|
-
const pk = row[
|
|
673
|
+
const pk = pkColNames.length === 1 ? row[pkColNames[0]] : pkColNames.map((name) => String(row[name])).join("||");
|
|
670
674
|
if (!grouped.has(pk)) {
|
|
671
675
|
const parentRow = {};
|
|
672
676
|
for (const [key, col] of parentEntries) {
|
|
@@ -784,8 +788,8 @@ class SingleJoinBuilderImpl {
|
|
|
784
788
|
}
|
|
785
789
|
#decodeJoinRow(rows) {
|
|
786
790
|
const parentEntries = columnEntries(this.#parent);
|
|
787
|
-
const
|
|
788
|
-
const
|
|
791
|
+
const pkKeys = findPKKeys(this.#parent);
|
|
792
|
+
const pkColNames = pkKeys.map((k) => getCol(this.#parent, k).name);
|
|
789
793
|
const childEntryMaps = [];
|
|
790
794
|
for (const j of this.#joins) {
|
|
791
795
|
childEntryMaps.push({
|
|
@@ -796,7 +800,7 @@ class SingleJoinBuilderImpl {
|
|
|
796
800
|
}
|
|
797
801
|
const grouped = new Map;
|
|
798
802
|
for (const r of rows) {
|
|
799
|
-
const pk = r[
|
|
803
|
+
const pk = pkColNames.length === 1 ? r[pkColNames[0]] : pkColNames.map((name) => String(r[name])).join("||");
|
|
800
804
|
if (!grouped.has(pk)) {
|
|
801
805
|
const parentRow = {};
|
|
802
806
|
for (const [key, col] of parentEntries) {
|
|
@@ -1456,7 +1460,28 @@ function table(name, columns, indexFn) {
|
|
|
1456
1460
|
const raw = indexFn(result);
|
|
1457
1461
|
if (raw.length > 0) {
|
|
1458
1462
|
const tableObj = result;
|
|
1459
|
-
|
|
1463
|
+
const indexes = [];
|
|
1464
|
+
let primaryKeyDef;
|
|
1465
|
+
for (const item of raw) {
|
|
1466
|
+
if (item && typeof item === "object" && "_type" in item) {
|
|
1467
|
+
if (item._type === "primaryKey") {
|
|
1468
|
+
primaryKeyDef = item.build();
|
|
1469
|
+
} else {
|
|
1470
|
+
indexes.push(item.build());
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
if (primaryKeyDef) {
|
|
1475
|
+
for (const [key, col] of Object.entries(columns)) {
|
|
1476
|
+
if (col.__internal.isPrimaryKey) {
|
|
1477
|
+
throw new Error(`Column "${key}" has primaryKey() but table "${name}" also defines a composite primaryKey(). Use one or the other.`);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
tableObj.__primaryKey = primaryKeyDef;
|
|
1481
|
+
}
|
|
1482
|
+
if (indexes.length > 0) {
|
|
1483
|
+
tableObj.__indexes = indexes;
|
|
1484
|
+
}
|
|
1460
1485
|
}
|
|
1461
1486
|
}
|
|
1462
1487
|
return result;
|
|
@@ -1530,10 +1555,28 @@ function snakeCaseTable(tableName, columns, indexFn) {
|
|
|
1530
1555
|
var snakeCase = {
|
|
1531
1556
|
table: snakeCaseTable
|
|
1532
1557
|
};
|
|
1558
|
+
function primaryKey() {
|
|
1559
|
+
let columns = [];
|
|
1560
|
+
const builder = {
|
|
1561
|
+
_type: "primaryKey",
|
|
1562
|
+
on(...cols) {
|
|
1563
|
+
columns = cols;
|
|
1564
|
+
return builder;
|
|
1565
|
+
},
|
|
1566
|
+
build() {
|
|
1567
|
+
if (columns.length === 0) {
|
|
1568
|
+
throw new Error("primaryKey() has no columns \u2014 call .on() before returning");
|
|
1569
|
+
}
|
|
1570
|
+
return { columns: columns.map((c) => c.name) };
|
|
1571
|
+
}
|
|
1572
|
+
};
|
|
1573
|
+
return builder;
|
|
1574
|
+
}
|
|
1533
1575
|
function index(name) {
|
|
1534
1576
|
let columns = [];
|
|
1535
1577
|
let isUnique = false;
|
|
1536
1578
|
const builder = {
|
|
1579
|
+
_type: "index",
|
|
1537
1580
|
on(...cols) {
|
|
1538
1581
|
columns = cols;
|
|
1539
1582
|
return builder;
|
|
@@ -1623,7 +1666,7 @@ function introspectColumns(client, tableName) {
|
|
|
1623
1666
|
const col = {
|
|
1624
1667
|
name: row.name,
|
|
1625
1668
|
sqlType: normalizeType(row.type),
|
|
1626
|
-
isPrimaryKey: row.pk
|
|
1669
|
+
isPrimaryKey: row.pk > 0,
|
|
1627
1670
|
isNotNull: row.notnull === 1,
|
|
1628
1671
|
isUnique: uniqueColumns.has(row.name),
|
|
1629
1672
|
hasDefault,
|
|
@@ -1639,7 +1682,8 @@ function introspectColumns(client, tableName) {
|
|
|
1639
1682
|
}
|
|
1640
1683
|
return col;
|
|
1641
1684
|
});
|
|
1642
|
-
|
|
1685
|
+
const pkColumns = rows.filter((row) => row.pk > 0).sort((a, b) => a.pk - b.pk).map((row) => row.name);
|
|
1686
|
+
return { columns, indexRows, primaryKeyColumns: pkColumns.length > 1 ? pkColumns : undefined };
|
|
1643
1687
|
}
|
|
1644
1688
|
function introspectIndexes(client, indexRows) {
|
|
1645
1689
|
const indexes = [];
|
|
@@ -1660,12 +1704,13 @@ function introspect(client) {
|
|
|
1660
1704
|
const tableRows = client.query(`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name != '__flint_migrations'`).all();
|
|
1661
1705
|
const tables = {};
|
|
1662
1706
|
for (const { name } of tableRows) {
|
|
1663
|
-
const { columns, indexRows } = introspectColumns(client, name);
|
|
1707
|
+
const { columns, indexRows, primaryKeyColumns } = introspectColumns(client, name);
|
|
1664
1708
|
const indexes = introspectIndexes(client, indexRows);
|
|
1665
1709
|
tables[name] = {
|
|
1666
1710
|
name,
|
|
1667
1711
|
columns,
|
|
1668
|
-
indexes
|
|
1712
|
+
indexes,
|
|
1713
|
+
primaryKeyColumns
|
|
1669
1714
|
};
|
|
1670
1715
|
}
|
|
1671
1716
|
return {
|
|
@@ -1680,6 +1725,7 @@ export {
|
|
|
1680
1725
|
sql,
|
|
1681
1726
|
snakeCase,
|
|
1682
1727
|
real,
|
|
1728
|
+
primaryKey,
|
|
1683
1729
|
or,
|
|
1684
1730
|
neq,
|
|
1685
1731
|
min,
|
|
@@ -97,7 +97,17 @@ function serializeTable(table) {
|
|
|
97
97
|
});
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
-
|
|
100
|
+
let primaryKeyColumns;
|
|
101
|
+
if (tableObj.__primaryKey) {
|
|
102
|
+
const pkDef = tableObj.__primaryKey;
|
|
103
|
+
primaryKeyColumns = pkDef.columns;
|
|
104
|
+
for (const col of columns) {
|
|
105
|
+
if (col.isPrimaryKey) {
|
|
106
|
+
throw new Error(`Column "${col.name}" has primaryKey() but table "${tableName}" also defines a composite primaryKey(). Use one or the other.`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { name: tableName, columns, indexes, primaryKeyColumns };
|
|
101
111
|
}
|
|
102
112
|
function serializeSchema(tables) {
|
|
103
113
|
const tableMap = {};
|
|
@@ -262,6 +272,11 @@ function diffTable(tableName, prev, curr) {
|
|
|
262
272
|
if (unsafe) {
|
|
263
273
|
return [rebuildTable(tableName, prev, curr)];
|
|
264
274
|
}
|
|
275
|
+
const prevPK = prev.primaryKeyColumns ?? [];
|
|
276
|
+
const currPK = curr.primaryKeyColumns ?? [];
|
|
277
|
+
if (JSON.stringify(prevPK) !== JSON.stringify(currPK)) {
|
|
278
|
+
return [rebuildTable(tableName, prev, curr)];
|
|
279
|
+
}
|
|
265
280
|
const ops = [...columnOps];
|
|
266
281
|
const prevIndexes = new Map(prev.indexes.map((i) => [i.name, i]));
|
|
267
282
|
const currIndexes = new Map(curr.indexes.map((i) => [i.name, i]));
|
|
@@ -466,9 +481,9 @@ __export(exports_sql, {
|
|
|
466
481
|
function sqlType(col) {
|
|
467
482
|
return col.sqlType.toUpperCase();
|
|
468
483
|
}
|
|
469
|
-
function columnToDDL(col) {
|
|
484
|
+
function columnToDDL(col, isCompositePK = false) {
|
|
470
485
|
const parts = [col.name, sqlType(col)];
|
|
471
|
-
if (col.isPrimaryKey)
|
|
486
|
+
if (col.isPrimaryKey && !isCompositePK)
|
|
472
487
|
parts.push("PRIMARY KEY");
|
|
473
488
|
if (col.isAutoIncrement === true)
|
|
474
489
|
parts.push("AUTOINCREMENT");
|
|
@@ -515,7 +530,12 @@ function rebuildTableToSQL(op) {
|
|
|
515
530
|
const tempName = `_flint_rebuild_${tableName}`;
|
|
516
531
|
const stmts = [];
|
|
517
532
|
stmts.push("PRAGMA defer_foreign_keys = 1");
|
|
518
|
-
const
|
|
533
|
+
const isComposite = newTable.primaryKeyColumns && newTable.primaryKeyColumns.length > 0;
|
|
534
|
+
const colDefs = newTable.columns.map((c) => columnToDDL(c, isComposite));
|
|
535
|
+
if (isComposite) {
|
|
536
|
+
colDefs.push(`PRIMARY KEY(${newTable.primaryKeyColumns.join(", ")})`);
|
|
537
|
+
}
|
|
538
|
+
const cols = colDefs.join(`,
|
|
519
539
|
`);
|
|
520
540
|
stmts.push(`CREATE TABLE ${tempName} (
|
|
521
541
|
${cols}
|
|
@@ -542,7 +562,12 @@ function rebuildTableToSQL(op) {
|
|
|
542
562
|
function operationToSQL(op) {
|
|
543
563
|
switch (op.type) {
|
|
544
564
|
case "addTable": {
|
|
545
|
-
const
|
|
565
|
+
const isComposite = op.table.primaryKeyColumns && op.table.primaryKeyColumns.length > 0;
|
|
566
|
+
const colDefs = op.table.columns.map((c) => columnToDDL(c, isComposite));
|
|
567
|
+
if (isComposite) {
|
|
568
|
+
colDefs.push(`PRIMARY KEY(${op.table.primaryKeyColumns.join(", ")})`);
|
|
569
|
+
}
|
|
570
|
+
const cols = colDefs.join(`,
|
|
546
571
|
`);
|
|
547
572
|
const stmts = [`CREATE TABLE ${op.table.name} (
|
|
548
573
|
${cols}
|