@pikku/migrator-sql 0.12.3 → 0.12.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @pikku/migrator-sql
2
2
 
3
+ ## 0.12.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 7262b4c: `db generate` now creates a referenced table before the one referencing it
8
+
3
9
  ## 0.12.3
4
10
 
5
11
  ### Patch Changes
@@ -10,4 +10,4 @@
10
10
  */
11
11
  export { migrate, baselineMigrations, pendingMigrations, MigrationDriftError, MIGRATION_TRACKING_TABLE, type MigrationExecutor, type MigrateResult, type AppliedMigration, } from './sql-migrator.js';
12
12
  export { assertSnakeCaseIdentifiers, findCamelCaseIdentifiers, stripSqlComments, CamelCaseIdentifierError, type CamelCaseIdentifier, } from './migration-identifiers.js';
13
- export { splitStatements, bareTableName, tableCreationSql, } from './schema-sql.js';
13
+ export { splitStatements, bareTableName, tableCreationSql, tablesInSourceOrder, } from './schema-sql.js';
package/dist/src/index.js CHANGED
@@ -10,4 +10,4 @@
10
10
  */
11
11
  export { migrate, baselineMigrations, pendingMigrations, MigrationDriftError, MIGRATION_TRACKING_TABLE, } from './sql-migrator.js';
12
12
  export { assertSnakeCaseIdentifiers, findCamelCaseIdentifiers, stripSqlComments, CamelCaseIdentifierError, } from './migration-identifiers.js';
13
- export { splitStatements, bareTableName, tableCreationSql, } from './schema-sql.js';
13
+ export { splitStatements, bareTableName, tableCreationSql, tablesInSourceOrder, } from './schema-sql.js';
@@ -41,3 +41,14 @@ export declare function bareTableName(name: string): string;
41
41
  * expected to fall back rather than emit nothing.
42
42
  */
43
43
  export declare function tableCreationSql(sql: string, table: string): string[];
44
+ /**
45
+ * `tables`, in the order `sql` creates them.
46
+ *
47
+ * A diff hands its missing tables back in whatever order it walked them, which
48
+ * is usually alphabetical and is never the order they can be created in: a
49
+ * table that references another has to come after it, and `channel_subscriptions`
50
+ * sorts before `channels`. The source's own SQL already has a workable order —
51
+ * it was applied in it — so that is the order used, with anything the source
52
+ * does not visibly create left at the end for the caller to render its own way.
53
+ */
54
+ export declare function tablesInSourceOrder(sql: string, tables: string[]): string[];
@@ -133,3 +133,27 @@ export function tableCreationSql(sql, table) {
133
133
  // and their presence says the table came from somewhere this cannot read.
134
134
  return creates ? statements : [];
135
135
  }
136
+ /**
137
+ * `tables`, in the order `sql` creates them.
138
+ *
139
+ * A diff hands its missing tables back in whatever order it walked them, which
140
+ * is usually alphabetical and is never the order they can be created in: a
141
+ * table that references another has to come after it, and `channel_subscriptions`
142
+ * sorts before `channels`. The source's own SQL already has a workable order —
143
+ * it was applied in it — so that is the order used, with anything the source
144
+ * does not visibly create left at the end for the caller to render its own way.
145
+ */
146
+ export function tablesInSourceOrder(sql, tables) {
147
+ const position = new Map();
148
+ let index = 0;
149
+ for (const statement of splitStatements(sql)) {
150
+ const create = CREATE_TABLE.exec(statement);
151
+ if (create) {
152
+ const name = bareTableName(create[1]);
153
+ if (!position.has(name))
154
+ position.set(name, index++);
155
+ }
156
+ }
157
+ const rank = (table) => position.get(bareTableName(table)) ?? Number.MAX_SAFE_INTEGER;
158
+ return [...tables].sort((a, b) => rank(a) - rank(b));
159
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/migrator-sql",
3
- "version": "0.12.3",
3
+ "version": "0.12.4",
4
4
  "description": "The SQL migration applier shared by the Pikku CLI and the standalone runtime",
5
5
  "author": "yasser.fadl@gmail.com",
6
6
  "license": "MIT",
package/src/index.ts CHANGED
@@ -31,4 +31,5 @@ export {
31
31
  splitStatements,
32
32
  bareTableName,
33
33
  tableCreationSql,
34
+ tablesInSourceOrder,
34
35
  } from './schema-sql.js'
@@ -1,7 +1,11 @@
1
1
  import test from 'node:test'
2
2
  import assert from 'node:assert/strict'
3
3
 
4
- import { splitStatements, tableCreationSql } from './schema-sql.js'
4
+ import {
5
+ splitStatements,
6
+ tableCreationSql,
7
+ tablesInSourceOrder,
8
+ } from './schema-sql.js'
5
9
 
6
10
  test('a semicolon inside a string literal is not a statement boundary', () => {
7
11
  const sql = `CREATE TABLE a (id TEXT, note TEXT DEFAULT 'one; two');
@@ -64,3 +68,25 @@ CREATE INDEX "orphan_idx" ON "orders" ("id");`
64
68
 
65
69
  assert.deepEqual(tableCreationSql(sql, 'orders'), [])
66
70
  })
71
+
72
+ test('a referencing table is created after the one it references, not alphabetically', () => {
73
+ const sql = `create table "channels" ("channel_id" text primary key);
74
+ create table "channel_subscriptions" ("channel_id" text not null references "channels" ("channel_id"));`
75
+
76
+ assert.deepEqual(
77
+ tablesInSourceOrder(sql, ['channel_subscriptions', 'channels']),
78
+ ['channels', 'channel_subscriptions']
79
+ )
80
+ })
81
+
82
+ test('a table the source does not create is left at the end', () => {
83
+ const sql = 'create table "a" ("id" text);'
84
+ assert.deepEqual(tablesInSourceOrder(sql, ['unknown', 'a']), ['a', 'unknown'])
85
+ })
86
+
87
+ test('source order survives a schema qualifier on either side', () => {
88
+ const sql = `create table app."b" ("id" text);
89
+ create table "a" ("id" text);`
90
+
91
+ assert.deepEqual(tablesInSourceOrder(sql, ['a', 'app.b']), ['app.b', 'a'])
92
+ })
package/src/schema-sql.ts CHANGED
@@ -153,3 +153,27 @@ export function tableCreationSql(sql: string, table: string): string[] {
153
153
  // and their presence says the table came from somewhere this cannot read.
154
154
  return creates ? statements : []
155
155
  }
156
+
157
+ /**
158
+ * `tables`, in the order `sql` creates them.
159
+ *
160
+ * A diff hands its missing tables back in whatever order it walked them, which
161
+ * is usually alphabetical and is never the order they can be created in: a
162
+ * table that references another has to come after it, and `channel_subscriptions`
163
+ * sorts before `channels`. The source's own SQL already has a workable order —
164
+ * it was applied in it — so that is the order used, with anything the source
165
+ * does not visibly create left at the end for the caller to render its own way.
166
+ */
167
+ export function tablesInSourceOrder(sql: string, tables: string[]): string[] {
168
+ const position = new Map<string, number>()
169
+ let index = 0
170
+ for (const statement of splitStatements(sql)) {
171
+ const create = CREATE_TABLE.exec(statement)
172
+ if (create) {
173
+ const name = bareTableName(create[1]!)
174
+ if (!position.has(name)) position.set(name, index++)
175
+ }
176
+ }
177
+ const rank = (table: string) => position.get(bareTableName(table)) ?? Number.MAX_SAFE_INTEGER
178
+ return [...tables].sort((a, b) => rank(a) - rank(b))
179
+ }