@stacksjs/database 0.70.241 → 0.70.243
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/migrations.d.ts +14 -0
- package/dist/migrations.js +67 -6
- package/package.json +10 -10
package/dist/migrations.d.ts
CHANGED
|
@@ -42,6 +42,20 @@ export declare function prepareMigrationModelsDir(): { modelsDir: string, skip:
|
|
|
42
42
|
* applied while its first column had never been created.
|
|
43
43
|
*/
|
|
44
44
|
export declare function sqlStatementsOf(content: string): string[];
|
|
45
|
+
/**
|
|
46
|
+
* Make a Postgres `CREATE TYPE … AS ENUM` statement safe to run twice.
|
|
47
|
+
*
|
|
48
|
+
* `CREATE TYPE` has no `IF NOT EXISTS`, so a corpus containing one can only be
|
|
49
|
+
* applied to a database that has never seen it. Every other way a migration run
|
|
50
|
+
* can be interrupted - a partially recorded ledger, a schema restored from a
|
|
51
|
+
* dump, a database built before the ledger existed - leaves `buddy migrate`
|
|
52
|
+
* dead on the first enum it meets, with an error naming a type that is already
|
|
53
|
+
* exactly right.
|
|
54
|
+
*
|
|
55
|
+
* The guard is a `DO` block catching `duplicate_object`, which needs a
|
|
56
|
+
* dollar-quoted body - hence the splitter above having to understand one.
|
|
57
|
+
*/
|
|
58
|
+
export declare function guardPostgresEnumTypes(sql: string): string;
|
|
45
59
|
export declare function preprocessSqliteMigrations(): void;
|
|
46
60
|
/**
|
|
47
61
|
* Public bootstrap entry point.
|
package/dist/migrations.js
CHANGED
|
@@ -96,9 +96,70 @@ export function prepareMigrationModelsDir() {
|
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
98
|
export function sqlStatementsOf(content) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
const statements = [];
|
|
100
|
+
let current = "", quote = null, dollarTag = null;
|
|
101
|
+
for (let i = 0;i < content.length; i++) {
|
|
102
|
+
const char = content[i];
|
|
103
|
+
if (dollarTag) {
|
|
104
|
+
current += char;
|
|
105
|
+
if (char === "$" && content.startsWith(dollarTag, i)) {
|
|
106
|
+
current += content.slice(i + 1, i + dollarTag.length);
|
|
107
|
+
i += dollarTag.length - 1;
|
|
108
|
+
dollarTag = null;
|
|
109
|
+
}
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (quote) {
|
|
113
|
+
current += char;
|
|
114
|
+
if (quote === "single" && char === "'" || quote === "double" && char === '"')
|
|
115
|
+
quote = null;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (char === "-" && content[i + 1] === "-") {
|
|
119
|
+
const newline = content.indexOf(`
|
|
120
|
+
`, i);
|
|
121
|
+
if (newline === -1)
|
|
122
|
+
break;
|
|
123
|
+
i = newline - 1;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const dollar = char === "$" ? /^\$[A-Za-z_]*\$/.exec(content.slice(i)) : null;
|
|
127
|
+
if (dollar) {
|
|
128
|
+
dollarTag = dollar[0];
|
|
129
|
+
current += dollarTag;
|
|
130
|
+
i += dollarTag.length - 1;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (char === "'") {
|
|
134
|
+
quote = "single";
|
|
135
|
+
current += char;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (char === '"') {
|
|
139
|
+
quote = "double";
|
|
140
|
+
current += char;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (char === ";") {
|
|
144
|
+
const trimmed = current.trim();
|
|
145
|
+
if (trimmed.length > 0)
|
|
146
|
+
statements.push(trimmed);
|
|
147
|
+
current = "";
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
current += char;
|
|
151
|
+
}
|
|
152
|
+
const trailing = current.trim();
|
|
153
|
+
if (trailing.length > 0)
|
|
154
|
+
statements.push(trailing);
|
|
155
|
+
return statements;
|
|
156
|
+
}
|
|
157
|
+
export function guardPostgresEnumTypes(sql) {
|
|
158
|
+
return sql.replace(/CREATE\s+TYPE\s+("?[\w.]+"?)\s+AS\s+ENUM\s*\(([^)]*)\)/gi, (match, name, members, offset, whole) => {
|
|
159
|
+
if (/\bBEGIN\s*$/i.test(whole.slice(Math.max(0, offset - 40), offset)))
|
|
160
|
+
return match;
|
|
161
|
+
return `DO $stacks$ BEGIN CREATE TYPE ${name} AS ENUM (${members}); EXCEPTION WHEN duplicate_object THEN null; END $stacks$`;
|
|
162
|
+
});
|
|
102
163
|
}
|
|
103
164
|
export function preprocessSqliteMigrations() {
|
|
104
165
|
const migrationsDir = join(process.cwd(), "database", "migrations");
|
|
@@ -484,9 +545,9 @@ function makeMigrationsIdempotent() {
|
|
|
484
545
|
} catch {
|
|
485
546
|
continue;
|
|
486
547
|
}
|
|
487
|
-
if (
|
|
548
|
+
if (!(/\bADD\s+(?:COLUMN|CONSTRAINT)\b/i.test(sql) || /\bCREATE\s+TYPE\b/i.test(sql)))
|
|
488
549
|
continue;
|
|
489
|
-
const next = idempotentSql(sql);
|
|
550
|
+
const next = guardPostgresEnumTypes(idempotentSql(sql));
|
|
490
551
|
if (next !== sql)
|
|
491
552
|
try {
|
|
492
553
|
writeFileSync(p, next);
|
|
@@ -814,7 +875,7 @@ export function createMissingEnumTypes(dangling, plan) {
|
|
|
814
875
|
if (!members?.length)
|
|
815
876
|
continue;
|
|
816
877
|
const literals = members.map((member) => `'${String(member).replaceAll("'", "''")}'`).join(", ");
|
|
817
|
-
statements.push(`
|
|
878
|
+
statements.push(`${guardPostgresEnumTypes(`CREATE TYPE "${name}" AS ENUM (${literals})`)};`);
|
|
818
879
|
defined.push(name);
|
|
819
880
|
}
|
|
820
881
|
return { statements, defined };
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/database",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.70.
|
|
5
|
+
"version": "0.70.243",
|
|
6
6
|
"description": "The Stacks database integration.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -58,15 +58,15 @@
|
|
|
58
58
|
"dynamodb-tooling": "^0.3.2"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@stacksjs/cli": "0.70.
|
|
62
|
-
"@stacksjs/config": "0.70.
|
|
63
|
-
"@stacksjs/logging": "0.70.
|
|
64
|
-
"@stacksjs/router": "0.70.
|
|
61
|
+
"@stacksjs/cli": "0.70.243",
|
|
62
|
+
"@stacksjs/config": "0.70.243",
|
|
63
|
+
"@stacksjs/logging": "0.70.243",
|
|
64
|
+
"@stacksjs/router": "0.70.243",
|
|
65
65
|
"better-dx": "^0.2.17",
|
|
66
|
-
"@stacksjs/path": "0.70.
|
|
67
|
-
"@stacksjs/query-builder": "0.70.
|
|
68
|
-
"@stacksjs/storage": "0.70.
|
|
69
|
-
"@stacksjs/strings": "0.70.
|
|
70
|
-
"@stacksjs/utils": "0.70.
|
|
66
|
+
"@stacksjs/path": "0.70.243",
|
|
67
|
+
"@stacksjs/query-builder": "0.70.243",
|
|
68
|
+
"@stacksjs/storage": "0.70.243",
|
|
69
|
+
"@stacksjs/strings": "0.70.243",
|
|
70
|
+
"@stacksjs/utils": "0.70.243"
|
|
71
71
|
}
|
|
72
72
|
}
|