rake-db 2.27.33 → 2.28.1
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/index.d.ts +8 -4
- package/dist/index.js +1529 -1506
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1531 -1509
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -17,6 +17,177 @@ const clearChanges = () => {
|
|
|
17
17
|
const getCurrentChanges = () => currentChanges;
|
|
18
18
|
const pushChange = (change) => currentChanges.push(change);
|
|
19
19
|
|
|
20
|
+
const RAKE_DB_LOCK_KEY = "8582141715823621641";
|
|
21
|
+
const getFirstWordAndRest = (input) => {
|
|
22
|
+
const i = input.search(/(?=[A-Z])|[-_ ]/);
|
|
23
|
+
if (i !== -1) {
|
|
24
|
+
const restStart = input[i] === "-" || input[i] === "_" || input[i] === " " ? i + 1 : i;
|
|
25
|
+
const rest = input.slice(restStart);
|
|
26
|
+
return [input.slice(0, i), rest[0].toLowerCase() + rest.slice(1)];
|
|
27
|
+
} else {
|
|
28
|
+
return [input];
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const getTextAfterRegExp = (input, regex, length) => {
|
|
32
|
+
let i = input.search(regex);
|
|
33
|
+
if (i === -1) return;
|
|
34
|
+
if (input[i] === "-" || input[i] === "_" || input[i] === " ") i++;
|
|
35
|
+
i += length;
|
|
36
|
+
const start = input[i] == "-" || input[i] === "_" || input[i] === " " ? i + 1 : i;
|
|
37
|
+
const text = input.slice(start);
|
|
38
|
+
return text[0].toLowerCase() + text.slice(1);
|
|
39
|
+
};
|
|
40
|
+
const getTextAfterTo = (input) => {
|
|
41
|
+
return getTextAfterRegExp(input, /(To|-to|_to| to)[A-Z-_ ]/, 2);
|
|
42
|
+
};
|
|
43
|
+
const getTextAfterFrom = (input) => {
|
|
44
|
+
return getTextAfterRegExp(input, /(From|-from|_from| from)[A-Z-_ ]/, 4);
|
|
45
|
+
};
|
|
46
|
+
const joinColumns = (columns) => {
|
|
47
|
+
return columns.map((column) => `"${column}"`).join(", ");
|
|
48
|
+
};
|
|
49
|
+
const quoteWithSchema = ({
|
|
50
|
+
schema,
|
|
51
|
+
name
|
|
52
|
+
}) => quoteTable(schema, name);
|
|
53
|
+
const quoteTable = (schema, table) => schema ? `"${schema}"."${table}"` : `"${table}"`;
|
|
54
|
+
const getSchemaAndTableFromName = (name) => {
|
|
55
|
+
const i = name.indexOf(".");
|
|
56
|
+
return i !== -1 ? [name.slice(0, i), name.slice(i + 1)] : [void 0, name];
|
|
57
|
+
};
|
|
58
|
+
const quoteNameFromString = (string) => {
|
|
59
|
+
return quoteTable(...getSchemaAndTableFromName(string));
|
|
60
|
+
};
|
|
61
|
+
const quoteCustomType = (s) => {
|
|
62
|
+
const [schema, type] = getSchemaAndTableFromName(s);
|
|
63
|
+
return schema ? '"' + schema + '".' + type : type;
|
|
64
|
+
};
|
|
65
|
+
const quoteSchemaTable = (arg, excludeCurrentSchema) => {
|
|
66
|
+
return pqb.singleQuote(concatSchemaAndName(arg, excludeCurrentSchema));
|
|
67
|
+
};
|
|
68
|
+
const concatSchemaAndName = ({
|
|
69
|
+
schema,
|
|
70
|
+
name
|
|
71
|
+
}, excludeCurrentSchema) => {
|
|
72
|
+
return schema && schema !== excludeCurrentSchema ? `${schema}.${name}` : name;
|
|
73
|
+
};
|
|
74
|
+
const makePopulateEnumQuery = (item) => {
|
|
75
|
+
const [schema, name] = getSchemaAndTableFromName(item.enumName);
|
|
76
|
+
return {
|
|
77
|
+
text: `SELECT unnest(enum_range(NULL::${quoteTable(schema, name)}))::text`,
|
|
78
|
+
then(result) {
|
|
79
|
+
item.options.push(...result.rows.map(([value]) => value));
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
const transaction = (adapter, fn) => {
|
|
84
|
+
return adapter.transaction(void 0, fn);
|
|
85
|
+
};
|
|
86
|
+
const queryLock = (trx) => trx.query(`SELECT pg_advisory_xact_lock('${RAKE_DB_LOCK_KEY}')`);
|
|
87
|
+
const getCliParam = (args, name) => {
|
|
88
|
+
if (args) {
|
|
89
|
+
const key = "--" + name;
|
|
90
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
91
|
+
const arg = args[i];
|
|
92
|
+
if (arg === key) return args[i + 1];
|
|
93
|
+
else if (arg.startsWith(key)) return arg.slice(key.length + 1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const migrationConfigDefaults = {
|
|
100
|
+
schemaConfig: pqb.defaultSchemaConfig,
|
|
101
|
+
migrationsPath: path.join("src", "db", "migrations"),
|
|
102
|
+
migrationId: { serial: 4 },
|
|
103
|
+
migrationsTable: "schemaMigrations",
|
|
104
|
+
snakeCase: false,
|
|
105
|
+
commands: {},
|
|
106
|
+
log: true,
|
|
107
|
+
logger: console,
|
|
108
|
+
import() {
|
|
109
|
+
throw new Error(
|
|
110
|
+
"Add `import: (path) => import(path),` setting to `rakeDb` config"
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const ensureMigrationsPath = (config) => {
|
|
115
|
+
if (!config.migrationsPath) {
|
|
116
|
+
config.migrationsPath = migrationConfigDefaults.migrationsPath;
|
|
117
|
+
}
|
|
118
|
+
if (!path.isAbsolute(config.migrationsPath)) {
|
|
119
|
+
config.migrationsPath = path.resolve(
|
|
120
|
+
config.basePath,
|
|
121
|
+
config.migrationsPath
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
return config;
|
|
125
|
+
};
|
|
126
|
+
const ensureBasePathAndDbScript = (config, intermediateCallers = 0) => {
|
|
127
|
+
if (config.basePath && config.dbScript) return config;
|
|
128
|
+
let filePath = pqb.getStackTrace()?.[3 + intermediateCallers]?.getFileName();
|
|
129
|
+
if (!filePath) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
"Failed to determine path to db script. Please set basePath option of rakeDb"
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
if (filePath.startsWith("file://")) {
|
|
135
|
+
filePath = node_url.fileURLToPath(filePath);
|
|
136
|
+
}
|
|
137
|
+
const ext = path.extname(filePath);
|
|
138
|
+
if (ext !== ".ts" && ext !== ".js" && ext !== ".mjs") {
|
|
139
|
+
throw new Error(
|
|
140
|
+
`Add a .ts suffix to the "${path.basename(filePath)}" when calling it`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
config.basePath = path.dirname(filePath);
|
|
144
|
+
config.dbScript = path.basename(filePath);
|
|
145
|
+
return config;
|
|
146
|
+
};
|
|
147
|
+
const processRakeDbConfig = (config, args) => {
|
|
148
|
+
const result = { ...migrationConfigDefaults, ...config };
|
|
149
|
+
if (!result.log) {
|
|
150
|
+
delete result.logger;
|
|
151
|
+
}
|
|
152
|
+
ensureBasePathAndDbScript(result, 1);
|
|
153
|
+
ensureMigrationsPath(result);
|
|
154
|
+
if (!result.recurrentPath) {
|
|
155
|
+
result.recurrentPath = path.join(
|
|
156
|
+
result.migrationsPath,
|
|
157
|
+
"recurrent"
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
if ("recurrentPath" in result && !path.isAbsolute(result.recurrentPath)) {
|
|
161
|
+
result.recurrentPath = path.resolve(result.basePath, result.recurrentPath);
|
|
162
|
+
}
|
|
163
|
+
if ("baseTable" in config && config.baseTable) {
|
|
164
|
+
const { types, snakeCase, language } = config.baseTable.prototype;
|
|
165
|
+
result.columnTypes = types || pqb.makeColumnTypes(pqb.defaultSchemaConfig);
|
|
166
|
+
if (snakeCase) result.snakeCase = true;
|
|
167
|
+
if (language) result.language = language;
|
|
168
|
+
} else {
|
|
169
|
+
const ct = "columnTypes" in config && config.columnTypes;
|
|
170
|
+
result.columnTypes = (typeof ct === "function" ? ct(
|
|
171
|
+
pqb.makeColumnTypes(pqb.defaultSchemaConfig)
|
|
172
|
+
) : ct) || pqb.makeColumnTypes(pqb.defaultSchemaConfig);
|
|
173
|
+
}
|
|
174
|
+
if (config.migrationId === "serial") {
|
|
175
|
+
result.migrationId = { serial: 4 };
|
|
176
|
+
}
|
|
177
|
+
const transaction = getCliParam(args, "transaction");
|
|
178
|
+
if (transaction) {
|
|
179
|
+
if (transaction !== "single" && transaction !== "per-migration") {
|
|
180
|
+
throw new Error(
|
|
181
|
+
`Unsupported transaction param ${transaction}, expected single or per-migration`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
result.transaction = transaction;
|
|
185
|
+
} else if (!result.transaction) {
|
|
186
|
+
result.transaction = "single";
|
|
187
|
+
}
|
|
188
|
+
return result;
|
|
189
|
+
};
|
|
190
|
+
|
|
20
191
|
const versionToString = (config, version) => config.migrationId === "timestamp" ? `${version}` : `${version}`.padStart(config.migrationId.serial, "0");
|
|
21
192
|
const columnTypeToSql = (item) => {
|
|
22
193
|
return item.data.isOfCustomType ? item instanceof pqb.DomainColumn ? quoteNameFromString(item.dataType) : quoteCustomType(item.toSQL()) : item.toSQL();
|
|
@@ -388,6 +559,17 @@ const cmpRawSql = (a, b) => {
|
|
|
388
559
|
const bValues = JSON.stringify(values);
|
|
389
560
|
return aSql === bSql && aValues === bValues;
|
|
390
561
|
};
|
|
562
|
+
const getMigrationsSchemaAndTable = (config) => {
|
|
563
|
+
const [tableSchema, table] = getSchemaAndTableFromName(
|
|
564
|
+
config.migrationsTable
|
|
565
|
+
);
|
|
566
|
+
const schema = tableSchema || (config.schema && config.schema !== "public" ? config.schema : void 0);
|
|
567
|
+
return { schema, table };
|
|
568
|
+
};
|
|
569
|
+
const migrationsSchemaTableSql = (config) => {
|
|
570
|
+
const { schema, table } = getMigrationsSchemaAndTable(config);
|
|
571
|
+
return `${schema ? `"${schema}".` : ""}"${table}"`;
|
|
572
|
+
};
|
|
391
573
|
|
|
392
574
|
const tableMethods = {
|
|
393
575
|
enum(name) {
|
|
@@ -1250,13 +1432,15 @@ const createView = async (migration, up, name, options, sql) => {
|
|
|
1250
1432
|
const query = astToQuery(ast);
|
|
1251
1433
|
await migration.adapter.arrays(interpolateSqlValues(query));
|
|
1252
1434
|
};
|
|
1253
|
-
const makeAst = (up,
|
|
1435
|
+
const makeAst = (up, fullName, options, sql) => {
|
|
1254
1436
|
if (typeof sql === "string") {
|
|
1255
1437
|
sql = pqb.raw({ raw: sql });
|
|
1256
1438
|
}
|
|
1439
|
+
const [schema, name] = getSchemaAndTableFromName(fullName);
|
|
1257
1440
|
return {
|
|
1258
1441
|
type: "view",
|
|
1259
1442
|
action: up ? "create" : "drop",
|
|
1443
|
+
schema,
|
|
1260
1444
|
name,
|
|
1261
1445
|
shape: {},
|
|
1262
1446
|
sql,
|
|
@@ -1268,12 +1452,13 @@ const astToQuery = (ast) => {
|
|
|
1268
1452
|
const values = [];
|
|
1269
1453
|
const sql = [];
|
|
1270
1454
|
const { options } = ast;
|
|
1455
|
+
const sqlName = `${ast.schema ? `"${ast.schema}".` : ""}"${ast.name}"`;
|
|
1271
1456
|
if (ast.action === "create") {
|
|
1272
1457
|
sql.push("CREATE");
|
|
1273
1458
|
if (options?.createOrReplace) sql.push("OR REPLACE");
|
|
1274
1459
|
if (options?.temporary) sql.push("TEMPORARY");
|
|
1275
1460
|
if (options?.recursive) sql.push("RECURSIVE");
|
|
1276
|
-
sql.push(`VIEW
|
|
1461
|
+
sql.push(`VIEW ${sqlName}`);
|
|
1277
1462
|
if (options?.columns) {
|
|
1278
1463
|
sql.push(
|
|
1279
1464
|
`(${options.columns.map((column) => `"${column}"`).join(", ")})`
|
|
@@ -1291,7 +1476,7 @@ const astToQuery = (ast) => {
|
|
|
1291
1476
|
} else {
|
|
1292
1477
|
sql.push("DROP VIEW");
|
|
1293
1478
|
if (options?.dropIfExists) sql.push(`IF EXISTS`);
|
|
1294
|
-
sql.push(
|
|
1479
|
+
sql.push(sqlName);
|
|
1295
1480
|
if (options?.dropMode) sql.push(options.dropMode);
|
|
1296
1481
|
}
|
|
1297
1482
|
return {
|
|
@@ -1302,7 +1487,6 @@ const astToQuery = (ast) => {
|
|
|
1302
1487
|
|
|
1303
1488
|
const createMigrationInterface = (tx, up, config) => {
|
|
1304
1489
|
const adapter = Object.create(tx);
|
|
1305
|
-
adapter.schema = adapter.getSchema() ?? "public";
|
|
1306
1490
|
const { query, arrays } = adapter;
|
|
1307
1491
|
const log = pqb.logParamToLogObject(config.logger || console, config.log);
|
|
1308
1492
|
adapter.query = (text, values) => {
|
|
@@ -2336,7 +2520,7 @@ const renameType = async (migration, from, to, kind) => {
|
|
|
2336
2520
|
}
|
|
2337
2521
|
if (ast.fromSchema !== ast.toSchema) {
|
|
2338
2522
|
await migration.adapter.query(
|
|
2339
|
-
`ALTER ${ast.kind} ${quoteTable(ast.fromSchema, ast.to)} SET SCHEMA "${ast.toSchema ?? migration.
|
|
2523
|
+
`ALTER ${ast.kind} ${quoteTable(ast.fromSchema, ast.to)} SET SCHEMA "${ast.toSchema ?? migration.options.schema ?? "public"}"`
|
|
2340
2524
|
);
|
|
2341
2525
|
}
|
|
2342
2526
|
};
|
|
@@ -2410,7 +2594,7 @@ const changeEnumValues = async (migration, enumName, fromValues, toValues) => {
|
|
|
2410
2594
|
);
|
|
2411
2595
|
};
|
|
2412
2596
|
const recreateEnum = async (migration, { schema, name }, values, errorMessage) => {
|
|
2413
|
-
const defaultSchema = migration.
|
|
2597
|
+
const defaultSchema = migration.options.schema ?? "public";
|
|
2414
2598
|
const quotedName = quoteTable(schema, name);
|
|
2415
2599
|
const relKinds = ["r", "m"];
|
|
2416
2600
|
const { rows: tables } = await migration.adapter.query(
|
|
@@ -2641,7 +2825,9 @@ ${arg === "timestamp" || digits !== 4 ? `Set \`migrationId\`: ${arg === "timesta
|
|
|
2641
2825
|
};
|
|
2642
2826
|
const renameMigrationVersionsInDb = async (config, adapter, values) => {
|
|
2643
2827
|
await adapter.arrays(
|
|
2644
|
-
`UPDATE
|
|
2828
|
+
`UPDATE ${migrationsSchemaTableSql(
|
|
2829
|
+
config
|
|
2830
|
+
)} AS t SET version = v.version FROM (VALUES ${values.map(
|
|
2645
2831
|
([oldVersion, , newVersion], i) => `('${oldVersion}', $${i + 1}, '${newVersion}')`
|
|
2646
2832
|
).join(
|
|
2647
2833
|
", "
|
|
@@ -2806,13 +2992,44 @@ function getDigitsPrefix(name) {
|
|
|
2806
2992
|
|
|
2807
2993
|
const saveMigratedVersion = async (db, version, name, config) => {
|
|
2808
2994
|
await db.silentArrays(
|
|
2809
|
-
`INSERT INTO
|
|
2995
|
+
`INSERT INTO ${migrationsSchemaTableSql(
|
|
2996
|
+
config
|
|
2997
|
+
)}(version, name) VALUES ($1, $2)`,
|
|
2810
2998
|
[version, name]
|
|
2811
2999
|
);
|
|
2812
3000
|
};
|
|
3001
|
+
const createMigrationsTable = async (db, config) => {
|
|
3002
|
+
const { schema } = getMigrationsSchemaAndTable(config);
|
|
3003
|
+
if (schema) {
|
|
3004
|
+
try {
|
|
3005
|
+
await db.query(`CREATE SCHEMA "${schema}"`);
|
|
3006
|
+
config.logger?.log(`Created schema ${schema}`);
|
|
3007
|
+
} catch (err) {
|
|
3008
|
+
if (err.code !== "42P06") {
|
|
3009
|
+
throw err;
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
try {
|
|
3014
|
+
await db.query(
|
|
3015
|
+
`CREATE TABLE ${migrationsSchemaTableSql(
|
|
3016
|
+
config
|
|
3017
|
+
)} ( version TEXT NOT NULL, name TEXT NOT NULL )`
|
|
3018
|
+
);
|
|
3019
|
+
config.logger?.log("Created versions table");
|
|
3020
|
+
} catch (err) {
|
|
3021
|
+
if (err.code === "42P07") {
|
|
3022
|
+
config.logger?.log("Versions table exists");
|
|
3023
|
+
} else {
|
|
3024
|
+
throw err;
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
};
|
|
2813
3028
|
const deleteMigratedVersion = async (db, version, name, config) => {
|
|
2814
3029
|
const res = await db.silentArrays(
|
|
2815
|
-
`DELETE FROM
|
|
3030
|
+
`DELETE FROM ${migrationsSchemaTableSql(
|
|
3031
|
+
config
|
|
3032
|
+
)} WHERE version = $1 AND name = $2`,
|
|
2816
3033
|
[version, name]
|
|
2817
3034
|
);
|
|
2818
3035
|
if (res.rowCount === 0) {
|
|
@@ -2823,7 +3040,7 @@ class NoMigrationsTableError extends Error {
|
|
|
2823
3040
|
}
|
|
2824
3041
|
const getMigratedVersionsMap = async (ctx, adapter, config, renameTo) => {
|
|
2825
3042
|
try {
|
|
2826
|
-
const table =
|
|
3043
|
+
const table = migrationsSchemaTableSql(config);
|
|
2827
3044
|
const result = await adapter.arrays(
|
|
2828
3045
|
`SELECT * FROM ${table} ORDER BY version`
|
|
2829
3046
|
);
|
|
@@ -2899,33 +3116,6 @@ async function renameMigrations(config, trx, versions, renameTo) {
|
|
|
2899
3116
|
return updatedVersions;
|
|
2900
3117
|
}
|
|
2901
3118
|
|
|
2902
|
-
const createMigrationsTable = async (db, config) => {
|
|
2903
|
-
const { schema } = db;
|
|
2904
|
-
if (schema && schema !== "public") {
|
|
2905
|
-
try {
|
|
2906
|
-
await db.query(`CREATE SCHEMA "${schema}"`);
|
|
2907
|
-
config.logger?.log(`Created schema ${schema}`);
|
|
2908
|
-
} catch (err) {
|
|
2909
|
-
if (err.code !== "42P06") {
|
|
2910
|
-
throw err;
|
|
2911
|
-
}
|
|
2912
|
-
}
|
|
2913
|
-
}
|
|
2914
|
-
try {
|
|
2915
|
-
await db.query(
|
|
2916
|
-
`CREATE TABLE "${config.migrationsTable}" ( version TEXT NOT NULL, name TEXT NOT NULL )`
|
|
2917
|
-
);
|
|
2918
|
-
config.logger?.log("Created versions table");
|
|
2919
|
-
} catch (err) {
|
|
2920
|
-
if (err.code === "42P07") {
|
|
2921
|
-
config.logger?.log("Versions table exists");
|
|
2922
|
-
} else {
|
|
2923
|
-
throw err;
|
|
2924
|
-
}
|
|
2925
|
-
}
|
|
2926
|
-
};
|
|
2927
|
-
|
|
2928
|
-
const RAKE_DB_LOCK_KEY = "8582141715823621641";
|
|
2929
3119
|
const transactionIfSingle = (params, fn) => {
|
|
2930
3120
|
return params.config.transaction === "single" ? transaction(params.adapter, fn) : fn(params.adapter);
|
|
2931
3121
|
};
|
|
@@ -3184,199 +3374,29 @@ const changeMigratedVersion = async (adapter, up, file, config) => {
|
|
|
3184
3374
|
);
|
|
3185
3375
|
};
|
|
3186
3376
|
|
|
3187
|
-
const
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3377
|
+
const ESC = "\x1B";
|
|
3378
|
+
const CSI = `${ESC}[`;
|
|
3379
|
+
const cursorShow = `${CSI}?25h`;
|
|
3380
|
+
const cursorHide = `${CSI}?25l`;
|
|
3381
|
+
const { stdin, stdout } = process;
|
|
3382
|
+
const visibleChars = (s) => s.replace(
|
|
3383
|
+
// eslint-disable-next-line no-control-regex
|
|
3384
|
+
/[\u001B\u009B][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d/#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PRZcf-ntqry=><~]))/g,
|
|
3385
|
+
""
|
|
3386
|
+
).length;
|
|
3387
|
+
const clear = (text) => {
|
|
3388
|
+
const rows = text.split(/\r?\n/).reduce(
|
|
3389
|
+
(rows2, line) => rows2 + 1 + Math.floor(Math.max(visibleChars(line) - 1, 0) / stdout.columns),
|
|
3390
|
+
0
|
|
3391
|
+
);
|
|
3392
|
+
let clear2 = "";
|
|
3393
|
+
for (let i = 0; i < rows; i++) {
|
|
3394
|
+
clear2 += `${CSI}2K`;
|
|
3395
|
+
if (i < rows - 1) {
|
|
3396
|
+
clear2 += `${CSI}${i < rows - 1 ? "1A" : "G"}`;
|
|
3397
|
+
}
|
|
3195
3398
|
}
|
|
3196
|
-
|
|
3197
|
-
const getTextAfterRegExp = (input, regex, length) => {
|
|
3198
|
-
let i = input.search(regex);
|
|
3199
|
-
if (i === -1) return;
|
|
3200
|
-
if (input[i] === "-" || input[i] === "_" || input[i] === " ") i++;
|
|
3201
|
-
i += length;
|
|
3202
|
-
const start = input[i] == "-" || input[i] === "_" || input[i] === " " ? i + 1 : i;
|
|
3203
|
-
const text = input.slice(start);
|
|
3204
|
-
return text[0].toLowerCase() + text.slice(1);
|
|
3205
|
-
};
|
|
3206
|
-
const getTextAfterTo = (input) => {
|
|
3207
|
-
return getTextAfterRegExp(input, /(To|-to|_to| to)[A-Z-_ ]/, 2);
|
|
3208
|
-
};
|
|
3209
|
-
const getTextAfterFrom = (input) => {
|
|
3210
|
-
return getTextAfterRegExp(input, /(From|-from|_from| from)[A-Z-_ ]/, 4);
|
|
3211
|
-
};
|
|
3212
|
-
const joinColumns = (columns) => {
|
|
3213
|
-
return columns.map((column) => `"${column}"`).join(", ");
|
|
3214
|
-
};
|
|
3215
|
-
const quoteWithSchema = ({
|
|
3216
|
-
schema,
|
|
3217
|
-
name
|
|
3218
|
-
}) => quoteTable(schema, name);
|
|
3219
|
-
const quoteTable = (schema, table) => schema ? `"${schema}"."${table}"` : `"${table}"`;
|
|
3220
|
-
const getSchemaAndTableFromName = (name) => {
|
|
3221
|
-
const i = name.indexOf(".");
|
|
3222
|
-
return i !== -1 ? [name.slice(0, i), name.slice(i + 1)] : [void 0, name];
|
|
3223
|
-
};
|
|
3224
|
-
const quoteNameFromString = (string) => {
|
|
3225
|
-
return quoteTable(...getSchemaAndTableFromName(string));
|
|
3226
|
-
};
|
|
3227
|
-
const quoteCustomType = (s) => {
|
|
3228
|
-
const [schema, type] = getSchemaAndTableFromName(s);
|
|
3229
|
-
return schema ? '"' + schema + '".' + type : type;
|
|
3230
|
-
};
|
|
3231
|
-
const quoteSchemaTable = (arg, excludeCurrentSchema) => {
|
|
3232
|
-
return pqb.singleQuote(concatSchemaAndName(arg, excludeCurrentSchema));
|
|
3233
|
-
};
|
|
3234
|
-
const concatSchemaAndName = ({
|
|
3235
|
-
schema,
|
|
3236
|
-
name
|
|
3237
|
-
}, excludeCurrentSchema) => {
|
|
3238
|
-
return schema && schema !== excludeCurrentSchema ? `${schema}.${name}` : name;
|
|
3239
|
-
};
|
|
3240
|
-
const makePopulateEnumQuery = (item) => {
|
|
3241
|
-
const [schema, name] = getSchemaAndTableFromName(item.enumName);
|
|
3242
|
-
return {
|
|
3243
|
-
text: `SELECT unnest(enum_range(NULL::${quoteTable(schema, name)}))::text`,
|
|
3244
|
-
then(result) {
|
|
3245
|
-
item.options.push(...result.rows.map(([value]) => value));
|
|
3246
|
-
}
|
|
3247
|
-
};
|
|
3248
|
-
};
|
|
3249
|
-
const transaction = (adapter, fn) => {
|
|
3250
|
-
return adapter.transaction(void 0, fn);
|
|
3251
|
-
};
|
|
3252
|
-
const queryLock = (trx) => trx.query(`SELECT pg_advisory_xact_lock('${RAKE_DB_LOCK_KEY}')`);
|
|
3253
|
-
const getCliParam = (args, name) => {
|
|
3254
|
-
if (args) {
|
|
3255
|
-
const key = "--" + name;
|
|
3256
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
3257
|
-
const arg = args[i];
|
|
3258
|
-
if (arg === key) return args[i + 1];
|
|
3259
|
-
else if (arg.startsWith(key)) return arg.slice(key.length + 1);
|
|
3260
|
-
}
|
|
3261
|
-
}
|
|
3262
|
-
return;
|
|
3263
|
-
};
|
|
3264
|
-
|
|
3265
|
-
const migrationConfigDefaults = {
|
|
3266
|
-
schemaConfig: pqb.defaultSchemaConfig,
|
|
3267
|
-
migrationsPath: path.join("src", "db", "migrations"),
|
|
3268
|
-
migrationId: { serial: 4 },
|
|
3269
|
-
migrationsTable: "schemaMigrations",
|
|
3270
|
-
snakeCase: false,
|
|
3271
|
-
commands: {},
|
|
3272
|
-
log: true,
|
|
3273
|
-
logger: console,
|
|
3274
|
-
import() {
|
|
3275
|
-
throw new Error(
|
|
3276
|
-
"Add `import: (path) => import(path),` setting to `rakeDb` config"
|
|
3277
|
-
);
|
|
3278
|
-
}
|
|
3279
|
-
};
|
|
3280
|
-
const ensureMigrationsPath = (config) => {
|
|
3281
|
-
if (!config.migrationsPath) {
|
|
3282
|
-
config.migrationsPath = migrationConfigDefaults.migrationsPath;
|
|
3283
|
-
}
|
|
3284
|
-
if (!path.isAbsolute(config.migrationsPath)) {
|
|
3285
|
-
config.migrationsPath = path.resolve(
|
|
3286
|
-
config.basePath,
|
|
3287
|
-
config.migrationsPath
|
|
3288
|
-
);
|
|
3289
|
-
}
|
|
3290
|
-
return config;
|
|
3291
|
-
};
|
|
3292
|
-
const ensureBasePathAndDbScript = (config, intermediateCallers = 0) => {
|
|
3293
|
-
if (config.basePath && config.dbScript) return config;
|
|
3294
|
-
let filePath = pqb.getStackTrace()?.[3 + intermediateCallers]?.getFileName();
|
|
3295
|
-
if (!filePath) {
|
|
3296
|
-
throw new Error(
|
|
3297
|
-
"Failed to determine path to db script. Please set basePath option of rakeDb"
|
|
3298
|
-
);
|
|
3299
|
-
}
|
|
3300
|
-
if (filePath.startsWith("file://")) {
|
|
3301
|
-
filePath = node_url.fileURLToPath(filePath);
|
|
3302
|
-
}
|
|
3303
|
-
const ext = path.extname(filePath);
|
|
3304
|
-
if (ext !== ".ts" && ext !== ".js" && ext !== ".mjs") {
|
|
3305
|
-
throw new Error(
|
|
3306
|
-
`Add a .ts suffix to the "${path.basename(filePath)}" when calling it`
|
|
3307
|
-
);
|
|
3308
|
-
}
|
|
3309
|
-
config.basePath = path.dirname(filePath);
|
|
3310
|
-
config.dbScript = path.basename(filePath);
|
|
3311
|
-
return config;
|
|
3312
|
-
};
|
|
3313
|
-
const processRakeDbConfig = (config, args) => {
|
|
3314
|
-
const result = { ...migrationConfigDefaults, ...config };
|
|
3315
|
-
if (!result.log) {
|
|
3316
|
-
delete result.logger;
|
|
3317
|
-
}
|
|
3318
|
-
ensureBasePathAndDbScript(result, 1);
|
|
3319
|
-
ensureMigrationsPath(result);
|
|
3320
|
-
if (!result.recurrentPath) {
|
|
3321
|
-
result.recurrentPath = path.join(
|
|
3322
|
-
result.migrationsPath,
|
|
3323
|
-
"recurrent"
|
|
3324
|
-
);
|
|
3325
|
-
}
|
|
3326
|
-
if ("recurrentPath" in result && !path.isAbsolute(result.recurrentPath)) {
|
|
3327
|
-
result.recurrentPath = path.resolve(result.basePath, result.recurrentPath);
|
|
3328
|
-
}
|
|
3329
|
-
if ("baseTable" in config && config.baseTable) {
|
|
3330
|
-
const { types, snakeCase, language } = config.baseTable.prototype;
|
|
3331
|
-
result.columnTypes = types || pqb.makeColumnTypes(pqb.defaultSchemaConfig);
|
|
3332
|
-
if (snakeCase) result.snakeCase = true;
|
|
3333
|
-
if (language) result.language = language;
|
|
3334
|
-
} else {
|
|
3335
|
-
const ct = "columnTypes" in config && config.columnTypes;
|
|
3336
|
-
result.columnTypes = (typeof ct === "function" ? ct(
|
|
3337
|
-
pqb.makeColumnTypes(pqb.defaultSchemaConfig)
|
|
3338
|
-
) : ct) || pqb.makeColumnTypes(pqb.defaultSchemaConfig);
|
|
3339
|
-
}
|
|
3340
|
-
if (config.migrationId === "serial") {
|
|
3341
|
-
result.migrationId = { serial: 4 };
|
|
3342
|
-
}
|
|
3343
|
-
const transaction = getCliParam(args, "transaction");
|
|
3344
|
-
if (transaction) {
|
|
3345
|
-
if (transaction !== "single" && transaction !== "per-migration") {
|
|
3346
|
-
throw new Error(
|
|
3347
|
-
`Unsupported transaction param ${transaction}, expected single or per-migration`
|
|
3348
|
-
);
|
|
3349
|
-
}
|
|
3350
|
-
result.transaction = transaction;
|
|
3351
|
-
} else if (!result.transaction) {
|
|
3352
|
-
result.transaction = "single";
|
|
3353
|
-
}
|
|
3354
|
-
return result;
|
|
3355
|
-
};
|
|
3356
|
-
|
|
3357
|
-
const ESC = "\x1B";
|
|
3358
|
-
const CSI = `${ESC}[`;
|
|
3359
|
-
const cursorShow = `${CSI}?25h`;
|
|
3360
|
-
const cursorHide = `${CSI}?25l`;
|
|
3361
|
-
const { stdin, stdout } = process;
|
|
3362
|
-
const visibleChars = (s) => s.replace(
|
|
3363
|
-
// eslint-disable-next-line no-control-regex
|
|
3364
|
-
/[\u001B\u009B][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d\/#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d\/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PRZcf-ntqry=><~]))/g,
|
|
3365
|
-
""
|
|
3366
|
-
).length;
|
|
3367
|
-
const clear = (text) => {
|
|
3368
|
-
const rows = text.split(/\r?\n/).reduce(
|
|
3369
|
-
(rows2, line) => rows2 + 1 + Math.floor(Math.max(visibleChars(line) - 1, 0) / stdout.columns),
|
|
3370
|
-
0
|
|
3371
|
-
);
|
|
3372
|
-
let clear2 = "";
|
|
3373
|
-
for (let i = 0; i < rows; i++) {
|
|
3374
|
-
clear2 += `${CSI}2K`;
|
|
3375
|
-
if (i < rows - 1) {
|
|
3376
|
-
clear2 += `${CSI}${i < rows - 1 ? "1A" : "G"}`;
|
|
3377
|
-
}
|
|
3378
|
-
}
|
|
3379
|
-
return clear2;
|
|
3399
|
+
return clear2;
|
|
3380
3400
|
};
|
|
3381
3401
|
const prompt = async ({
|
|
3382
3402
|
render,
|
|
@@ -3686,962 +3706,6 @@ const readdirRecursive = async (dirPath, cb) => {
|
|
|
3686
3706
|
);
|
|
3687
3707
|
};
|
|
3688
3708
|
|
|
3689
|
-
const filterSchema = (table) => `${table} !~ '^pg_' AND ${table} != 'information_schema'`;
|
|
3690
|
-
const jsonAgg = (sql2, as) => `(SELECT coalesce(json_agg(t.*), '[]') FROM (${sql2}) t) AS "${as}"`;
|
|
3691
|
-
const columnsSql = ({
|
|
3692
|
-
schema,
|
|
3693
|
-
table,
|
|
3694
|
-
join = "",
|
|
3695
|
-
where
|
|
3696
|
-
}) => `SELECT
|
|
3697
|
-
${schema}.nspname "schemaName",
|
|
3698
|
-
${table}.relname "tableName",
|
|
3699
|
-
a.attname "name",
|
|
3700
|
-
t.typname "type",
|
|
3701
|
-
tn.nspname "typeSchema",
|
|
3702
|
-
a.attndims "arrayDims",
|
|
3703
|
-
information_schema._pg_char_max_length(tt.id, tt.mod) "maxChars",
|
|
3704
|
-
information_schema._pg_numeric_precision(tt.id, tt.mod) "numericPrecision",
|
|
3705
|
-
information_schema._pg_numeric_scale(tt.id,tt.mod) "numericScale",
|
|
3706
|
-
information_schema._pg_datetime_precision(tt.id,tt.mod) "dateTimePrecision",
|
|
3707
|
-
CAST(
|
|
3708
|
-
CASE WHEN a.attgenerated = ''
|
|
3709
|
-
THEN pg_get_expr(ad.adbin, ad.adrelid)
|
|
3710
|
-
END AS information_schema.character_data
|
|
3711
|
-
) AS "default",
|
|
3712
|
-
NOT (a.attnotnull OR (t.typtype = 'd' AND t.typnotnull)) AS "isNullable",
|
|
3713
|
-
co.collname AS "collate",
|
|
3714
|
-
NULLIF(a.attcompression, '') AS compression,
|
|
3715
|
-
pgd.description AS "comment",
|
|
3716
|
-
(
|
|
3717
|
-
CASE WHEN a.attidentity IN ('a', 'd') THEN (
|
|
3718
|
-
json_build_object(
|
|
3719
|
-
'always',
|
|
3720
|
-
a.attidentity = 'a',
|
|
3721
|
-
'start',
|
|
3722
|
-
seq.seqstart,
|
|
3723
|
-
'increment',
|
|
3724
|
-
seq.seqincrement,
|
|
3725
|
-
'min',
|
|
3726
|
-
nullif(seq.seqmin, 1),
|
|
3727
|
-
'max',
|
|
3728
|
-
nullif(seq.seqmax, (
|
|
3729
|
-
CASE t.typname
|
|
3730
|
-
WHEN 'int2' THEN 32767
|
|
3731
|
-
WHEN 'int4' THEN 2147483647
|
|
3732
|
-
WHEN 'int8' THEN 9223372036854775807
|
|
3733
|
-
ELSE NULL
|
|
3734
|
-
END
|
|
3735
|
-
)),
|
|
3736
|
-
'cache',
|
|
3737
|
-
seq.seqcache,
|
|
3738
|
-
'cycle',
|
|
3739
|
-
seq.seqcycle
|
|
3740
|
-
)
|
|
3741
|
-
) END
|
|
3742
|
-
) "identity",
|
|
3743
|
-
ext.extname "extension",
|
|
3744
|
-
a.atttypmod "typmod"
|
|
3745
|
-
FROM pg_attribute a
|
|
3746
|
-
${join}
|
|
3747
|
-
LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum
|
|
3748
|
-
JOIN pg_type t
|
|
3749
|
-
ON t.oid = (
|
|
3750
|
-
CASE WHEN a.attndims = 0
|
|
3751
|
-
THEN a.atttypid
|
|
3752
|
-
ELSE (SELECT t.typelem FROM pg_type t WHERE t.oid = a.atttypid)
|
|
3753
|
-
END
|
|
3754
|
-
)
|
|
3755
|
-
JOIN LATERAL (
|
|
3756
|
-
SELECT
|
|
3757
|
-
CASE WHEN t.typtype = 'd' THEN t.typbasetype ELSE t.oid END id,
|
|
3758
|
-
CASE WHEN t.typtype = 'd' THEN t.typtypmod ELSE a.atttypmod END mod
|
|
3759
|
-
) tt ON true
|
|
3760
|
-
JOIN pg_namespace tn ON tn.oid = t.typnamespace
|
|
3761
|
-
LEFT JOIN (pg_collation co JOIN pg_namespace nco ON (co.collnamespace = nco.oid))
|
|
3762
|
-
ON a.attcollation = co.oid AND (nco.nspname, co.collname) <> ('pg_catalog', 'default')
|
|
3763
|
-
LEFT JOIN pg_catalog.pg_description pgd
|
|
3764
|
-
ON pgd.objoid = a.attrelid
|
|
3765
|
-
AND pgd.objsubid = a.attnum
|
|
3766
|
-
LEFT JOIN (pg_depend dep JOIN pg_sequence seq ON (dep.classid = 'pg_class'::regclass AND dep.objid = seq.seqrelid AND dep.deptype = 'i'))
|
|
3767
|
-
ON (dep.refclassid = 'pg_class'::regclass AND dep.refobjid = ${table}.oid AND dep.refobjsubid = a.attnum)
|
|
3768
|
-
LEFT JOIN pg_depend d ON d.objid = t.oid AND d.classid = 'pg_type'::regclass AND d.deptype = 'e'
|
|
3769
|
-
LEFT JOIN pg_extension ext ON ext.oid = d.refobjid
|
|
3770
|
-
WHERE a.attnum > 0
|
|
3771
|
-
AND NOT a.attisdropped
|
|
3772
|
-
AND ${where}
|
|
3773
|
-
ORDER BY a.attnum`;
|
|
3774
|
-
const schemasSql = `SELECT coalesce(json_agg(nspname ORDER BY nspname), '[]')
|
|
3775
|
-
FROM pg_catalog.pg_namespace n
|
|
3776
|
-
WHERE ${filterSchema("nspname")}`;
|
|
3777
|
-
const tablesSql = `SELECT
|
|
3778
|
-
nspname AS "schemaName",
|
|
3779
|
-
relname AS "name",
|
|
3780
|
-
obj_description(c.oid) AS comment,
|
|
3781
|
-
(SELECT coalesce(json_agg(t), '[]') FROM (${columnsSql({
|
|
3782
|
-
schema: "n",
|
|
3783
|
-
table: "c",
|
|
3784
|
-
where: "a.attrelid = c.oid"
|
|
3785
|
-
})}) t) AS "columns"
|
|
3786
|
-
FROM pg_class c
|
|
3787
|
-
JOIN pg_catalog.pg_namespace n ON n.oid = relnamespace
|
|
3788
|
-
WHERE (relkind = 'r' OR relkind = 'p')
|
|
3789
|
-
AND ${filterSchema("nspname")}
|
|
3790
|
-
ORDER BY relname`;
|
|
3791
|
-
const viewsSql = `SELECT
|
|
3792
|
-
nc.nspname AS "schemaName",
|
|
3793
|
-
c.relname AS "name",
|
|
3794
|
-
(
|
|
3795
|
-
SELECT COALESCE(json_agg(t.*), '[]')
|
|
3796
|
-
FROM (
|
|
3797
|
-
SELECT
|
|
3798
|
-
ns.nspname AS "schemaName",
|
|
3799
|
-
obj.relname AS "name"
|
|
3800
|
-
FROM pg_class obj
|
|
3801
|
-
JOIN pg_depend dep ON dep.refobjid = obj.oid
|
|
3802
|
-
JOIN pg_rewrite rew ON rew.oid = dep.objid
|
|
3803
|
-
JOIN pg_namespace ns ON ns.oid = obj.relnamespace
|
|
3804
|
-
WHERE rew.ev_class = c.oid AND obj.oid <> c.oid
|
|
3805
|
-
) t
|
|
3806
|
-
) "deps",
|
|
3807
|
-
right(substring(r.ev_action from ':hasRecursive w'), 1)::bool AS "isRecursive",
|
|
3808
|
-
array_to_json(c.reloptions) AS "with",
|
|
3809
|
-
(SELECT coalesce(json_agg(t), '[]') FROM (${columnsSql({
|
|
3810
|
-
schema: "nc",
|
|
3811
|
-
table: "c",
|
|
3812
|
-
where: "a.attrelid = c.oid"
|
|
3813
|
-
})}) t) AS "columns",
|
|
3814
|
-
pg_get_viewdef(c.oid) AS "sql"
|
|
3815
|
-
FROM pg_namespace nc
|
|
3816
|
-
JOIN pg_class c
|
|
3817
|
-
ON nc.oid = c.relnamespace
|
|
3818
|
-
AND c.relkind = 'v'
|
|
3819
|
-
AND c.relpersistence != 't'
|
|
3820
|
-
JOIN pg_rewrite r ON r.ev_class = c.oid
|
|
3821
|
-
WHERE ${filterSchema("nc.nspname")}
|
|
3822
|
-
ORDER BY c.relname`;
|
|
3823
|
-
const indexesSql = `SELECT
|
|
3824
|
-
n.nspname "schemaName",
|
|
3825
|
-
t.relname "tableName",
|
|
3826
|
-
ic.relname "name",
|
|
3827
|
-
am.amname AS "using",
|
|
3828
|
-
i.indisunique "unique",
|
|
3829
|
-
(
|
|
3830
|
-
SELECT json_agg(
|
|
3831
|
-
(
|
|
3832
|
-
CASE WHEN t.e = 0
|
|
3833
|
-
THEN jsonb_build_object('expression', pg_get_indexdef(i.indexrelid, t.i::int4, false))
|
|
3834
|
-
ELSE jsonb_build_object('column', (
|
|
3835
|
-
(
|
|
3836
|
-
SELECT attname
|
|
3837
|
-
FROM pg_catalog.pg_attribute
|
|
3838
|
-
WHERE attrelid = i.indrelid
|
|
3839
|
-
AND attnum = t.e
|
|
3840
|
-
)
|
|
3841
|
-
))
|
|
3842
|
-
END
|
|
3843
|
-
) || (
|
|
3844
|
-
CASE WHEN i.indcollation[t.i - 1] = 0
|
|
3845
|
-
THEN '{}'::jsonb
|
|
3846
|
-
ELSE (
|
|
3847
|
-
SELECT (
|
|
3848
|
-
CASE WHEN collname = 'default'
|
|
3849
|
-
THEN '{}'::jsonb
|
|
3850
|
-
ELSE jsonb_build_object('collate', collname)
|
|
3851
|
-
END
|
|
3852
|
-
)
|
|
3853
|
-
FROM pg_catalog.pg_collation
|
|
3854
|
-
WHERE oid = i.indcollation[t.i - 1]
|
|
3855
|
-
)
|
|
3856
|
-
END
|
|
3857
|
-
) || (
|
|
3858
|
-
SELECT
|
|
3859
|
-
CASE WHEN opcdefault AND attoptions IS NULL
|
|
3860
|
-
THEN '{}'::jsonb
|
|
3861
|
-
ELSE jsonb_build_object(
|
|
3862
|
-
'opclass', opcname || COALESCE('(' || array_to_string(attoptions, ', ') || ')', '')
|
|
3863
|
-
)
|
|
3864
|
-
END
|
|
3865
|
-
FROM pg_opclass
|
|
3866
|
-
LEFT JOIN pg_attribute
|
|
3867
|
-
ON attrelid = i.indexrelid
|
|
3868
|
-
AND attnum = t.i
|
|
3869
|
-
WHERE oid = i.indclass[t.i - 1]
|
|
3870
|
-
) || (
|
|
3871
|
-
CASE WHEN i.indoption[t.i - 1] = 0
|
|
3872
|
-
THEN '{}'::jsonb
|
|
3873
|
-
ELSE jsonb_build_object(
|
|
3874
|
-
'order',
|
|
3875
|
-
CASE
|
|
3876
|
-
WHEN i.indoption[t.i - 1] = 1 THEN 'DESC NULLS LAST'
|
|
3877
|
-
WHEN i.indoption[t.i - 1] = 2 THEN 'ASC NULLS FIRST'
|
|
3878
|
-
WHEN i.indoption[t.i - 1] = 3 THEN 'DESC'
|
|
3879
|
-
ELSE NULL
|
|
3880
|
-
END
|
|
3881
|
-
)
|
|
3882
|
-
END
|
|
3883
|
-
)
|
|
3884
|
-
)
|
|
3885
|
-
FROM unnest(i.indkey[:indnkeyatts - 1]) WITH ORDINALITY AS t(e, i)
|
|
3886
|
-
) "columns",
|
|
3887
|
-
(
|
|
3888
|
-
SELECT json_agg(
|
|
3889
|
-
(
|
|
3890
|
-
SELECT attname
|
|
3891
|
-
FROM pg_catalog.pg_attribute
|
|
3892
|
-
WHERE attrelid = i.indrelid
|
|
3893
|
-
AND attnum = j.e
|
|
3894
|
-
)
|
|
3895
|
-
)
|
|
3896
|
-
FROM unnest(i.indkey[indnkeyatts:]) AS j(e)
|
|
3897
|
-
) AS "include",
|
|
3898
|
-
(to_jsonb(i.*)->'indnullsnotdistinct')::bool AS "nullsNotDistinct",
|
|
3899
|
-
NULLIF(pg_catalog.array_to_string(
|
|
3900
|
-
ic.reloptions || array(SELECT 'toast.' || x FROM pg_catalog.unnest(tc.reloptions) x),
|
|
3901
|
-
', '
|
|
3902
|
-
), '') AS "with",
|
|
3903
|
-
(
|
|
3904
|
-
SELECT tablespace
|
|
3905
|
-
FROM pg_indexes
|
|
3906
|
-
WHERE schemaname = n.nspname
|
|
3907
|
-
AND indexname = ic.relname
|
|
3908
|
-
) AS tablespace,
|
|
3909
|
-
pg_get_expr(i.indpred, i.indrelid) AS "where",
|
|
3910
|
-
(
|
|
3911
|
-
CASE i.indisexclusion WHEN true
|
|
3912
|
-
THEN (
|
|
3913
|
-
SELECT json_agg(o.oprname)
|
|
3914
|
-
FROM pg_catalog.pg_constraint c, LATERAL unnest(c.conexclop) op_oid
|
|
3915
|
-
JOIN pg_operator o ON o.oid = op_oid
|
|
3916
|
-
WHERE c.conindid = ic.oid
|
|
3917
|
-
)
|
|
3918
|
-
END
|
|
3919
|
-
) "exclude"
|
|
3920
|
-
FROM pg_index i
|
|
3921
|
-
JOIN pg_class t ON t.oid = i.indrelid
|
|
3922
|
-
JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
3923
|
-
JOIN pg_class ic ON ic.oid = i.indexrelid
|
|
3924
|
-
JOIN pg_am am ON am.oid = ic.relam
|
|
3925
|
-
LEFT JOIN pg_catalog.pg_class tc ON (ic.reltoastrelid = tc.oid)
|
|
3926
|
-
WHERE ${filterSchema("n.nspname")}
|
|
3927
|
-
AND NOT i.indisprimary
|
|
3928
|
-
ORDER BY ic.relname`;
|
|
3929
|
-
const constraintsSql = `SELECT
|
|
3930
|
-
s.nspname AS "schemaName",
|
|
3931
|
-
t.relname AS "tableName",
|
|
3932
|
-
c.conname AS "name",
|
|
3933
|
-
(
|
|
3934
|
-
SELECT json_agg(ccu.column_name)
|
|
3935
|
-
FROM information_schema.constraint_column_usage ccu
|
|
3936
|
-
WHERE contype = 'p'
|
|
3937
|
-
AND ccu.constraint_name = c.conname
|
|
3938
|
-
AND ccu.table_schema = s.nspname
|
|
3939
|
-
) AS "primaryKey",
|
|
3940
|
-
(
|
|
3941
|
-
SELECT
|
|
3942
|
-
json_build_object(
|
|
3943
|
-
'foreignSchema',
|
|
3944
|
-
fs.nspname,
|
|
3945
|
-
'foreignTable',
|
|
3946
|
-
ft.relname,
|
|
3947
|
-
'columns',
|
|
3948
|
-
(
|
|
3949
|
-
SELECT json_agg(ccu.column_name)
|
|
3950
|
-
FROM information_schema.key_column_usage ccu
|
|
3951
|
-
WHERE ccu.constraint_name = c.conname
|
|
3952
|
-
AND ccu.table_schema = cs.nspname
|
|
3953
|
-
),
|
|
3954
|
-
'foreignColumns',
|
|
3955
|
-
(
|
|
3956
|
-
SELECT json_agg(ccu.column_name)
|
|
3957
|
-
FROM information_schema.constraint_column_usage ccu
|
|
3958
|
-
WHERE ccu.constraint_name = c.conname
|
|
3959
|
-
AND ccu.table_schema = cs.nspname
|
|
3960
|
-
),
|
|
3961
|
-
'match',
|
|
3962
|
-
c.confmatchtype,
|
|
3963
|
-
'onUpdate',
|
|
3964
|
-
c.confupdtype,
|
|
3965
|
-
'onDelete',
|
|
3966
|
-
c.confdeltype
|
|
3967
|
-
)
|
|
3968
|
-
FROM pg_class ft
|
|
3969
|
-
JOIN pg_catalog.pg_namespace fs ON fs.oid = ft.relnamespace
|
|
3970
|
-
JOIN pg_catalog.pg_namespace cs ON cs.oid = c.connamespace
|
|
3971
|
-
WHERE contype = 'f' AND ft.oid = confrelid
|
|
3972
|
-
) AS "references",
|
|
3973
|
-
(
|
|
3974
|
-
SELECT
|
|
3975
|
-
CASE conbin IS NULL
|
|
3976
|
-
WHEN false THEN
|
|
3977
|
-
json_build_object(
|
|
3978
|
-
'columns',
|
|
3979
|
-
json_agg(ccu.column_name),
|
|
3980
|
-
'expression',
|
|
3981
|
-
pg_get_expr(conbin, conrelid)
|
|
3982
|
-
)
|
|
3983
|
-
END
|
|
3984
|
-
FROM information_schema.constraint_column_usage ccu
|
|
3985
|
-
WHERE conbin IS NOT NULL
|
|
3986
|
-
AND ccu.constraint_name = c.conname
|
|
3987
|
-
AND ccu.table_schema = s.nspname
|
|
3988
|
-
) AS "check"
|
|
3989
|
-
FROM pg_catalog.pg_constraint c
|
|
3990
|
-
JOIN pg_class t ON t.oid = conrelid
|
|
3991
|
-
JOIN pg_catalog.pg_namespace s
|
|
3992
|
-
ON s.oid = t.relnamespace
|
|
3993
|
-
AND contype IN ('p', 'f', 'c')
|
|
3994
|
-
AND ${filterSchema("s.nspname")}
|
|
3995
|
-
ORDER BY c.conname`;
|
|
3996
|
-
const triggersSql = `SELECT event_object_schema AS "schemaName",
|
|
3997
|
-
event_object_table AS "tableName",
|
|
3998
|
-
trigger_schema AS "triggerSchema",
|
|
3999
|
-
trigger_name AS name,
|
|
4000
|
-
json_agg(event_manipulation) AS events,
|
|
4001
|
-
action_timing AS activation,
|
|
4002
|
-
action_condition AS condition,
|
|
4003
|
-
action_statement AS definition
|
|
4004
|
-
FROM information_schema.triggers
|
|
4005
|
-
WHERE ${filterSchema("event_object_schema")}
|
|
4006
|
-
GROUP BY event_object_schema, event_object_table, trigger_schema, trigger_name, action_timing, action_condition, action_statement
|
|
4007
|
-
ORDER BY trigger_name`;
|
|
4008
|
-
const extensionsSql = `SELECT
|
|
4009
|
-
nspname AS "schemaName",
|
|
4010
|
-
extname AS "name",
|
|
4011
|
-
extversion AS version
|
|
4012
|
-
FROM pg_extension
|
|
4013
|
-
JOIN pg_catalog.pg_namespace n ON n.oid = extnamespace
|
|
4014
|
-
AND ${filterSchema("n.nspname")}`;
|
|
4015
|
-
const enumsSql = `SELECT
|
|
4016
|
-
n.nspname as "schemaName",
|
|
4017
|
-
t.typname as name,
|
|
4018
|
-
json_agg(e.enumlabel ORDER BY e.enumsortorder) as values
|
|
4019
|
-
FROM pg_type t
|
|
4020
|
-
JOIN pg_enum e ON t.oid = e.enumtypid
|
|
4021
|
-
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
4022
|
-
WHERE ${filterSchema("n.nspname")}
|
|
4023
|
-
GROUP BY n.nspname, t.typname`;
|
|
4024
|
-
const domainsSql = `SELECT
|
|
4025
|
-
n.nspname AS "schemaName",
|
|
4026
|
-
d.typname AS "name",
|
|
4027
|
-
t.typname AS "type",
|
|
4028
|
-
s.nspname AS "typeSchema",
|
|
4029
|
-
NOT d.typnotnull AS "isNullable",
|
|
4030
|
-
d.typndims AS "arrayDims",
|
|
4031
|
-
character_maximum_length AS "maxChars",
|
|
4032
|
-
numeric_precision AS "numericPrecision",
|
|
4033
|
-
numeric_scale AS "numericScale",
|
|
4034
|
-
datetime_precision AS "dateTimePrecision",
|
|
4035
|
-
collation_name AS "collate",
|
|
4036
|
-
domain_default AS "default",
|
|
4037
|
-
(
|
|
4038
|
-
SELECT json_agg(pg_get_expr(conbin, conrelid))
|
|
4039
|
-
FROM pg_catalog.pg_constraint c
|
|
4040
|
-
WHERE c.contypid = d.oid
|
|
4041
|
-
) AS "checks"
|
|
4042
|
-
FROM pg_catalog.pg_type d
|
|
4043
|
-
JOIN pg_catalog.pg_namespace n ON n.oid = d.typnamespace
|
|
4044
|
-
JOIN information_schema.domains i
|
|
4045
|
-
ON i.domain_schema = nspname
|
|
4046
|
-
AND i.domain_name = d.typname
|
|
4047
|
-
JOIN pg_catalog.pg_type t
|
|
4048
|
-
ON (
|
|
4049
|
-
CASE WHEN d.typcategory = 'A'
|
|
4050
|
-
THEN t.typarray
|
|
4051
|
-
ELSE t.oid
|
|
4052
|
-
END
|
|
4053
|
-
) = d.typbasetype
|
|
4054
|
-
JOIN pg_catalog.pg_namespace s ON s.oid = t.typnamespace
|
|
4055
|
-
WHERE d.typtype = 'd' AND ${filterSchema("n.nspname")}`;
|
|
4056
|
-
const collationsSql = (version) => `SELECT
|
|
4057
|
-
nspname "schemaName",
|
|
4058
|
-
collname "name",
|
|
4059
|
-
CASE WHEN collprovider = 'i' THEN 'icu' WHEN collprovider = 'c' THEN 'libc' ELSE collprovider::text END "provider",
|
|
4060
|
-
collisdeterministic "deterministic",
|
|
4061
|
-
collcollate "lcCollate",
|
|
4062
|
-
collctype "lcCType",
|
|
4063
|
-
${version >= 17 ? "colllocale" : "colliculocale"} "locale",
|
|
4064
|
-
collversion "version"
|
|
4065
|
-
FROM pg_collation
|
|
4066
|
-
JOIN pg_namespace n on pg_collation.collnamespace = n.oid
|
|
4067
|
-
WHERE ${filterSchema("n.nspname")}`;
|
|
4068
|
-
const sql = (version) => `SELECT (${schemasSql}) AS "schemas", ${jsonAgg(
|
|
4069
|
-
tablesSql,
|
|
4070
|
-
"tables"
|
|
4071
|
-
)}, ${jsonAgg(viewsSql, "views")}, ${jsonAgg(
|
|
4072
|
-
indexesSql,
|
|
4073
|
-
"indexes"
|
|
4074
|
-
)}, ${jsonAgg(constraintsSql, "constraints")}, ${jsonAgg(
|
|
4075
|
-
triggersSql,
|
|
4076
|
-
"triggers"
|
|
4077
|
-
)}, ${jsonAgg(extensionsSql, "extensions")}, ${jsonAgg(
|
|
4078
|
-
enumsSql,
|
|
4079
|
-
"enums"
|
|
4080
|
-
)}, ${jsonAgg(domainsSql, "domains")}, ${jsonAgg(
|
|
4081
|
-
collationsSql(version),
|
|
4082
|
-
"collations"
|
|
4083
|
-
)}`;
|
|
4084
|
-
async function introspectDbSchema(db) {
|
|
4085
|
-
const {
|
|
4086
|
-
rows: [{ version: versionString }]
|
|
4087
|
-
} = await db.query("SELECT version()");
|
|
4088
|
-
const version = +versionString.match(/\d+/)[0];
|
|
4089
|
-
const data = await db.query(sql(version));
|
|
4090
|
-
const result = data.rows[0];
|
|
4091
|
-
for (const domain of result.domains) {
|
|
4092
|
-
domain.checks = domain.checks?.filter((check) => check);
|
|
4093
|
-
nullsToUndefined(domain);
|
|
4094
|
-
}
|
|
4095
|
-
for (const table of result.tables) {
|
|
4096
|
-
for (const column of table.columns) {
|
|
4097
|
-
nullsToUndefined(column);
|
|
4098
|
-
if (column.identity) nullsToUndefined(column.identity);
|
|
4099
|
-
if (column.compression) {
|
|
4100
|
-
column.compression = column.compression === "p" ? "pglz" : "lz4";
|
|
4101
|
-
}
|
|
4102
|
-
}
|
|
4103
|
-
}
|
|
4104
|
-
const indexes = [];
|
|
4105
|
-
const excludes = [];
|
|
4106
|
-
for (const index of result.indexes) {
|
|
4107
|
-
nullsToUndefined(index);
|
|
4108
|
-
for (const column of index.columns) {
|
|
4109
|
-
if (!("expression" in column)) continue;
|
|
4110
|
-
const s = column.expression;
|
|
4111
|
-
const columnR = `"?\\w+"?`;
|
|
4112
|
-
const langR = `(${columnR}|'\\w+'::regconfig)`;
|
|
4113
|
-
const firstColumnR = `[(]*${columnR}`;
|
|
4114
|
-
const concatR = `\\|\\|`;
|
|
4115
|
-
const restColumnR = ` ${concatR} ' '::text\\) ${concatR} ${columnR}\\)`;
|
|
4116
|
-
const coalesceColumn = `COALESCE\\(${columnR}, ''::text\\)`;
|
|
4117
|
-
const tsVectorR = `to_tsvector\\(${langR}, (${firstColumnR}|${restColumnR}|${coalesceColumn})+\\)`;
|
|
4118
|
-
const weightR = `'\\w'::"char"`;
|
|
4119
|
-
const setWeightR = `setweight\\(${tsVectorR}, ${weightR}\\)`;
|
|
4120
|
-
const setWeightOrTsVectorR = `(${setWeightR}|${tsVectorR})`;
|
|
4121
|
-
const match = s.match(
|
|
4122
|
-
new RegExp(`^([\\(]*${setWeightOrTsVectorR}[\\)]*( ${concatR} )?)+$`)
|
|
4123
|
-
);
|
|
4124
|
-
if (!match) continue;
|
|
4125
|
-
let language;
|
|
4126
|
-
let languageColumn;
|
|
4127
|
-
const tokens = match[0].match(
|
|
4128
|
-
new RegExp(
|
|
4129
|
-
`setweight\\(|to_tsvector\\(${langR}|[:']?${columnR}\\(?`,
|
|
4130
|
-
"g"
|
|
4131
|
-
)
|
|
4132
|
-
)?.reduce((acc, token) => {
|
|
4133
|
-
if (token === "setweight(" || token === "COALESCE(" || token[0] === ":")
|
|
4134
|
-
return acc;
|
|
4135
|
-
if (token.startsWith("to_tsvector(")) {
|
|
4136
|
-
if (token[12] === "'") {
|
|
4137
|
-
language = token.slice(13, -12);
|
|
4138
|
-
} else {
|
|
4139
|
-
languageColumn = token.slice(12);
|
|
4140
|
-
}
|
|
4141
|
-
} else if (token[0] === "'") {
|
|
4142
|
-
acc.push({ kind: "weight", value: token[1] });
|
|
4143
|
-
} else {
|
|
4144
|
-
if (token[0] === '"') token = token.slice(1, -1);
|
|
4145
|
-
acc.push({ kind: "column", value: token });
|
|
4146
|
-
}
|
|
4147
|
-
return acc;
|
|
4148
|
-
}, []);
|
|
4149
|
-
if (!tokens) continue;
|
|
4150
|
-
index.language = language;
|
|
4151
|
-
index.languageColumn = languageColumn;
|
|
4152
|
-
index.tsVector = true;
|
|
4153
|
-
index.columns = [];
|
|
4154
|
-
for (const token of tokens) {
|
|
4155
|
-
if (token.kind === "column") {
|
|
4156
|
-
index.columns.push({
|
|
4157
|
-
column: token.value
|
|
4158
|
-
});
|
|
4159
|
-
} else if (token.kind === "weight") {
|
|
4160
|
-
index.columns[index.columns.length - 1].weight = token.value;
|
|
4161
|
-
}
|
|
4162
|
-
}
|
|
4163
|
-
}
|
|
4164
|
-
(index.exclude ? excludes : indexes).push(index);
|
|
4165
|
-
}
|
|
4166
|
-
result.indexes = indexes;
|
|
4167
|
-
result.excludes = excludes;
|
|
4168
|
-
return result;
|
|
4169
|
-
}
|
|
4170
|
-
const nullsToUndefined = (obj) => {
|
|
4171
|
-
for (const key in obj) {
|
|
4172
|
-
if (obj[key] === null)
|
|
4173
|
-
obj[key] = void 0;
|
|
4174
|
-
}
|
|
4175
|
-
};
|
|
4176
|
-
|
|
4177
|
-
const matchMap = {
|
|
4178
|
-
s: void 0,
|
|
4179
|
-
f: "FULL",
|
|
4180
|
-
p: "PARTIAL"
|
|
4181
|
-
};
|
|
4182
|
-
const fkeyActionMap = {
|
|
4183
|
-
a: void 0,
|
|
4184
|
-
// default
|
|
4185
|
-
r: "RESTRICT",
|
|
4186
|
-
c: "CASCADE",
|
|
4187
|
-
n: "SET NULL",
|
|
4188
|
-
d: "SET DEFAULT"
|
|
4189
|
-
};
|
|
4190
|
-
const makeStructureToAstCtx = (config, currentSchema) => ({
|
|
4191
|
-
snakeCase: config.snakeCase,
|
|
4192
|
-
unsupportedTypes: {},
|
|
4193
|
-
currentSchema,
|
|
4194
|
-
columnSchemaConfig: config.schemaConfig,
|
|
4195
|
-
columnsByType: pqb.makeColumnsByType(config.schemaConfig)
|
|
4196
|
-
});
|
|
4197
|
-
const structureToAst = async (ctx, adapter, config) => {
|
|
4198
|
-
const ast = [];
|
|
4199
|
-
const data = await introspectDbSchema(adapter);
|
|
4200
|
-
for (const name of data.schemas) {
|
|
4201
|
-
if (name === "public") continue;
|
|
4202
|
-
ast.push({
|
|
4203
|
-
type: "schema",
|
|
4204
|
-
action: "create",
|
|
4205
|
-
name
|
|
4206
|
-
});
|
|
4207
|
-
}
|
|
4208
|
-
for (const it of data.collations) {
|
|
4209
|
-
ast.push({
|
|
4210
|
-
type: "collation",
|
|
4211
|
-
action: "create",
|
|
4212
|
-
...it,
|
|
4213
|
-
schema: it.schemaName === ctx.currentSchema ? void 0 : it.schemaName
|
|
4214
|
-
});
|
|
4215
|
-
}
|
|
4216
|
-
const domains = makeDomainsMap(ctx, data);
|
|
4217
|
-
for (const table of data.tables) {
|
|
4218
|
-
if (table.name === config.migrationsTable) continue;
|
|
4219
|
-
ast.push(tableToAst(ctx, data, table, "create", domains));
|
|
4220
|
-
}
|
|
4221
|
-
for (const it of data.extensions) {
|
|
4222
|
-
ast.push({
|
|
4223
|
-
type: "extension",
|
|
4224
|
-
action: "create",
|
|
4225
|
-
name: it.name,
|
|
4226
|
-
schema: it.schemaName === ctx.currentSchema ? void 0 : it.schemaName,
|
|
4227
|
-
version: it.version
|
|
4228
|
-
});
|
|
4229
|
-
}
|
|
4230
|
-
for (const it of data.enums) {
|
|
4231
|
-
ast.push({
|
|
4232
|
-
type: "enum",
|
|
4233
|
-
action: "create",
|
|
4234
|
-
name: it.name,
|
|
4235
|
-
schema: it.schemaName === ctx.currentSchema ? void 0 : it.schemaName,
|
|
4236
|
-
values: it.values
|
|
4237
|
-
});
|
|
4238
|
-
}
|
|
4239
|
-
for (const it of data.domains) {
|
|
4240
|
-
ast.push({
|
|
4241
|
-
type: "domain",
|
|
4242
|
-
action: "create",
|
|
4243
|
-
schema: it.schemaName === ctx.currentSchema ? void 0 : it.schemaName,
|
|
4244
|
-
name: it.name,
|
|
4245
|
-
baseType: domains[`${it.schemaName}.${it.name}`]
|
|
4246
|
-
});
|
|
4247
|
-
}
|
|
4248
|
-
for (const table of data.tables) {
|
|
4249
|
-
for (const fkey of data.constraints) {
|
|
4250
|
-
if (fkey.references && fkey.tableName === table.name && fkey.schemaName === table.schemaName && checkIfIsOuterRecursiveFkey(data, table, fkey.references)) {
|
|
4251
|
-
ast.push({
|
|
4252
|
-
...constraintToAst(ctx, fkey),
|
|
4253
|
-
type: "constraint",
|
|
4254
|
-
action: "create",
|
|
4255
|
-
tableSchema: table.schemaName === ctx.currentSchema ? void 0 : table.schemaName,
|
|
4256
|
-
tableName: fkey.tableName
|
|
4257
|
-
});
|
|
4258
|
-
}
|
|
4259
|
-
}
|
|
4260
|
-
}
|
|
4261
|
-
for (const view of data.views) {
|
|
4262
|
-
ast.push(viewToAst(ctx, data, domains, view));
|
|
4263
|
-
}
|
|
4264
|
-
return ast;
|
|
4265
|
-
};
|
|
4266
|
-
const makeDomainsMap = (ctx, data) => {
|
|
4267
|
-
const domains = {};
|
|
4268
|
-
for (const it of data.domains) {
|
|
4269
|
-
const column = instantiateDbColumn(ctx, data, domains, {
|
|
4270
|
-
schemaName: it.schemaName,
|
|
4271
|
-
name: it.name,
|
|
4272
|
-
type: it.type,
|
|
4273
|
-
typeSchema: it.typeSchema,
|
|
4274
|
-
arrayDims: it.arrayDims,
|
|
4275
|
-
tableName: "",
|
|
4276
|
-
isNullable: it.isNullable,
|
|
4277
|
-
collate: it.collate,
|
|
4278
|
-
default: it.default,
|
|
4279
|
-
typmod: -1
|
|
4280
|
-
});
|
|
4281
|
-
if (it.checks) {
|
|
4282
|
-
column.data.checks = it.checks.map((check) => ({
|
|
4283
|
-
sql: new pqb.RawSql([[check]])
|
|
4284
|
-
}));
|
|
4285
|
-
}
|
|
4286
|
-
domains[`${it.schemaName}.${it.name}`] = column;
|
|
4287
|
-
}
|
|
4288
|
-
return domains;
|
|
4289
|
-
};
|
|
4290
|
-
const getDbColumnIsSerial = (item) => {
|
|
4291
|
-
if (item.type === "int2" || item.type === "int4" || item.type === "int8") {
|
|
4292
|
-
const { default: def, schemaName, tableName, name } = item;
|
|
4293
|
-
const seq = `${tableName}_${name}_seq`;
|
|
4294
|
-
if (def && (def === `nextval(${pqb.singleQuote(`${seq}`)}::regclass)` || def === `nextval(${pqb.singleQuote(`"${seq}"`)}::regclass)` || def === `nextval(${pqb.singleQuote(`${schemaName}.${seq}`)}::regclass)` || def === `nextval(${pqb.singleQuote(`"${schemaName}".${seq}`)}::regclass)` || def === `nextval(${pqb.singleQuote(`${schemaName}."${seq}"`)}::regclass)` || def === `nextval(${pqb.singleQuote(`"${schemaName}"."${seq}"`)}::regclass)`)) {
|
|
4295
|
-
return true;
|
|
4296
|
-
}
|
|
4297
|
-
}
|
|
4298
|
-
return false;
|
|
4299
|
-
};
|
|
4300
|
-
const instantiateDbColumn = (ctx, data, domains, dbColumn) => {
|
|
4301
|
-
var _a, _b;
|
|
4302
|
-
const isSerial = getDbColumnIsSerial(dbColumn);
|
|
4303
|
-
if (isSerial) {
|
|
4304
|
-
dbColumn = { ...dbColumn, default: void 0 };
|
|
4305
|
-
}
|
|
4306
|
-
let column;
|
|
4307
|
-
const col = instantiateColumnByDbType(ctx, dbColumn.type, isSerial, dbColumn);
|
|
4308
|
-
if (col) {
|
|
4309
|
-
column = col;
|
|
4310
|
-
} else {
|
|
4311
|
-
const { typeSchema, type: typeName } = dbColumn;
|
|
4312
|
-
const typeId = typeSchema === "pg_catalog" ? typeName : `${typeSchema}.${typeName}`;
|
|
4313
|
-
const domainColumn = domains[typeId];
|
|
4314
|
-
if (domainColumn) {
|
|
4315
|
-
column = new pqb.DomainColumn(
|
|
4316
|
-
ctx.columnSchemaConfig,
|
|
4317
|
-
typeName,
|
|
4318
|
-
typeSchema,
|
|
4319
|
-
dbColumn.extension
|
|
4320
|
-
).as(domainColumn);
|
|
4321
|
-
} else {
|
|
4322
|
-
const enumType = data.enums.find(
|
|
4323
|
-
(x) => x.name === typeName && x.schemaName === typeSchema
|
|
4324
|
-
);
|
|
4325
|
-
if (enumType) {
|
|
4326
|
-
column = new pqb.EnumColumn(
|
|
4327
|
-
ctx.columnSchemaConfig,
|
|
4328
|
-
typeSchema === ctx.currentSchema ? typeName : typeId,
|
|
4329
|
-
enumType.values,
|
|
4330
|
-
ctx.columnSchemaConfig.type
|
|
4331
|
-
);
|
|
4332
|
-
} else {
|
|
4333
|
-
column = new pqb.CustomTypeColumn(
|
|
4334
|
-
ctx.columnSchemaConfig,
|
|
4335
|
-
typeName,
|
|
4336
|
-
typeSchema === "pg_catalog" ? void 0 : typeSchema,
|
|
4337
|
-
dbColumn.extension
|
|
4338
|
-
);
|
|
4339
|
-
((_a = ctx.unsupportedTypes)[_b = dbColumn.type] ?? (_a[_b] = [])).push(
|
|
4340
|
-
`${dbColumn.schemaName}${dbColumn.tableName ? `.${dbColumn.tableName}` : ""}.${dbColumn.name}`
|
|
4341
|
-
);
|
|
4342
|
-
}
|
|
4343
|
-
pqb.assignDbDataToColumn(column, dbColumn);
|
|
4344
|
-
}
|
|
4345
|
-
}
|
|
4346
|
-
column.data.name = void 0;
|
|
4347
|
-
if (!column.data.isNullable) column.data.isNullable = void 0;
|
|
4348
|
-
if (dbColumn.arrayDims) {
|
|
4349
|
-
const arr = new pqb.ArrayColumn(
|
|
4350
|
-
ctx.columnSchemaConfig,
|
|
4351
|
-
column,
|
|
4352
|
-
ctx.columnSchemaConfig.type
|
|
4353
|
-
);
|
|
4354
|
-
arr.data.isNullable = dbColumn.isNullable;
|
|
4355
|
-
arr.data.arrayDims = dbColumn.arrayDims;
|
|
4356
|
-
column = arr;
|
|
4357
|
-
}
|
|
4358
|
-
return column;
|
|
4359
|
-
};
|
|
4360
|
-
const instantiateColumnByDbType = (ctx, type, isSerial, params) => {
|
|
4361
|
-
let columnFn = ctx.columnsByType[!isSerial ? type : type === "int2" ? "smallserial" : type === "int4" ? "serial" : "bigserial"];
|
|
4362
|
-
if (!columnFn && params.extension === "postgis" && type === "geography" && pqb.PostgisGeographyPointColumn.isDefaultPoint(params.typmod)) {
|
|
4363
|
-
columnFn = ctx.columnsByType.geographyDefaultPoint;
|
|
4364
|
-
}
|
|
4365
|
-
return columnFn ? pqb.assignDbDataToColumn(columnFn(), params) : void 0;
|
|
4366
|
-
};
|
|
4367
|
-
const tableToAst = (ctx, data, table, action, domains) => {
|
|
4368
|
-
const { schemaName, name: tableName } = table;
|
|
4369
|
-
const tableData = getDbStructureTableData(data, table);
|
|
4370
|
-
const { primaryKey, constraints } = tableData;
|
|
4371
|
-
return {
|
|
4372
|
-
type: "table",
|
|
4373
|
-
action,
|
|
4374
|
-
schema: schemaName === ctx.currentSchema ? void 0 : schemaName,
|
|
4375
|
-
comment: table.comment,
|
|
4376
|
-
name: tableName,
|
|
4377
|
-
shape: makeDbStructureColumnsShape(ctx, data, domains, table, tableData),
|
|
4378
|
-
noPrimaryKey: tableData.primaryKey ? "error" : "ignore",
|
|
4379
|
-
primaryKey: primaryKey && primaryKey.columns.length > 1 ? { ...primaryKey, columns: primaryKey.columns.map(pqb.toCamelCase) } : void 0,
|
|
4380
|
-
indexes: indexesOrExcludesToAst(
|
|
4381
|
-
tableName,
|
|
4382
|
-
tableData,
|
|
4383
|
-
"indexes"
|
|
4384
|
-
),
|
|
4385
|
-
excludes: indexesOrExcludesToAst(
|
|
4386
|
-
tableName,
|
|
4387
|
-
tableData,
|
|
4388
|
-
"excludes"
|
|
4389
|
-
),
|
|
4390
|
-
constraints: constraints.reduce((acc, it) => {
|
|
4391
|
-
if (it.check && it.references || it.check && it.check.columns?.length !== 1 || it.references && it.references.columns.length !== 1 && !checkIfIsOuterRecursiveFkey(data, table, it.references)) {
|
|
4392
|
-
acc.push(dbConstraintToTableConstraint(ctx, table, it));
|
|
4393
|
-
}
|
|
4394
|
-
return acc;
|
|
4395
|
-
}, [])
|
|
4396
|
-
};
|
|
4397
|
-
};
|
|
4398
|
-
const indexesOrExcludesToAst = (tableName, tableData, key) => {
|
|
4399
|
-
return tableData[key].reduce((acc, item) => {
|
|
4400
|
-
if (item.columns.length > 1 || item.columns.some((it) => "expression" in it)) {
|
|
4401
|
-
const options = makeIndexOrExcludeOptions(tableName, item, key);
|
|
4402
|
-
acc.push({
|
|
4403
|
-
columns: item.columns.map((it, i) => ({
|
|
4404
|
-
with: "exclude" in item && item.exclude ? item.exclude[i] : void 0,
|
|
4405
|
-
..."expression" in it ? { expression: it.expression } : { column: pqb.toCamelCase(it.column) },
|
|
4406
|
-
collate: it.collate,
|
|
4407
|
-
opclass: it.opclass,
|
|
4408
|
-
order: it.order
|
|
4409
|
-
})),
|
|
4410
|
-
options: {
|
|
4411
|
-
...options,
|
|
4412
|
-
include: item.include?.map(pqb.toCamelCase)
|
|
4413
|
-
}
|
|
4414
|
-
});
|
|
4415
|
-
}
|
|
4416
|
-
return acc;
|
|
4417
|
-
}, []);
|
|
4418
|
-
};
|
|
4419
|
-
const getDbStructureTableData = (data, { name, schemaName }) => {
|
|
4420
|
-
const filterFn = filterByTableSchema(name, schemaName);
|
|
4421
|
-
const constraints = data.constraints.filter(filterFn);
|
|
4422
|
-
const primaryKey = constraints.find((c) => c.primaryKey);
|
|
4423
|
-
return {
|
|
4424
|
-
primaryKey: primaryKey?.primaryKey ? {
|
|
4425
|
-
columns: primaryKey.primaryKey,
|
|
4426
|
-
name: primaryKey.name === `${name}_pkey` ? void 0 : primaryKey.name
|
|
4427
|
-
} : void 0,
|
|
4428
|
-
indexes: data.indexes.filter(filterFn),
|
|
4429
|
-
excludes: data.excludes.filter(filterFn),
|
|
4430
|
-
constraints
|
|
4431
|
-
};
|
|
4432
|
-
};
|
|
4433
|
-
const filterByTableSchema = (tableName, schemaName) => (x) => x.tableName === tableName && x.schemaName === schemaName;
|
|
4434
|
-
const constraintToAst = (ctx, item) => {
|
|
4435
|
-
const result = {};
|
|
4436
|
-
const { references, check } = item;
|
|
4437
|
-
if (references) {
|
|
4438
|
-
const options = {};
|
|
4439
|
-
result.references = {
|
|
4440
|
-
columns: references.columns,
|
|
4441
|
-
fnOrTable: getReferencesTable(ctx, references),
|
|
4442
|
-
foreignColumns: references.foreignColumns,
|
|
4443
|
-
options
|
|
4444
|
-
};
|
|
4445
|
-
const match = matchMap[references.match];
|
|
4446
|
-
if (match) options.match = match;
|
|
4447
|
-
const onUpdate = fkeyActionMap[references.onUpdate];
|
|
4448
|
-
if (onUpdate) options.onUpdate = onUpdate;
|
|
4449
|
-
const onDelete = fkeyActionMap[references.onDelete];
|
|
4450
|
-
if (onDelete) options.onDelete = onDelete;
|
|
4451
|
-
}
|
|
4452
|
-
if (check) {
|
|
4453
|
-
result.check = pqb.raw({ raw: check.expression });
|
|
4454
|
-
}
|
|
4455
|
-
if (item.name && item.name !== getConstraintName(item.tableName, result, ctx.snakeCase)) {
|
|
4456
|
-
result.name = item.name;
|
|
4457
|
-
if (result.references?.options) {
|
|
4458
|
-
result.references.options.name = item.name;
|
|
4459
|
-
}
|
|
4460
|
-
}
|
|
4461
|
-
return result;
|
|
4462
|
-
};
|
|
4463
|
-
const getReferencesTable = (ctx, references) => {
|
|
4464
|
-
return references.foreignSchema !== ctx.currentSchema ? `${references.foreignSchema}.${references.foreignTable}` : references.foreignTable;
|
|
4465
|
-
};
|
|
4466
|
-
const isColumnCheck = (it) => {
|
|
4467
|
-
return !it.references && it.check?.columns?.length === 1;
|
|
4468
|
-
};
|
|
4469
|
-
const viewToAst = (ctx, data, domains, view) => {
|
|
4470
|
-
const shape = makeDbStructureColumnsShape(ctx, data, domains, view);
|
|
4471
|
-
const options = {};
|
|
4472
|
-
if (view.isRecursive) options.recursive = true;
|
|
4473
|
-
if (view.with) {
|
|
4474
|
-
const withOptions = {};
|
|
4475
|
-
options.with = withOptions;
|
|
4476
|
-
for (const pair of view.with) {
|
|
4477
|
-
const [key, value] = pair.split("=");
|
|
4478
|
-
withOptions[pqb.toCamelCase(key)] = value === "true" ? true : value === "false" ? false : value;
|
|
4479
|
-
}
|
|
4480
|
-
}
|
|
4481
|
-
return {
|
|
4482
|
-
type: "view",
|
|
4483
|
-
action: "create",
|
|
4484
|
-
schema: view.schemaName === ctx.currentSchema ? void 0 : view.schemaName,
|
|
4485
|
-
name: view.name,
|
|
4486
|
-
shape,
|
|
4487
|
-
sql: pqb.raw({ raw: view.sql }),
|
|
4488
|
-
options,
|
|
4489
|
-
deps: view.deps
|
|
4490
|
-
};
|
|
4491
|
-
};
|
|
4492
|
-
const makeDbStructureColumnsShape = (ctx, data, domains, table, tableData) => {
|
|
4493
|
-
const shape = {};
|
|
4494
|
-
const checks = tableData ? getDbTableColumnsChecks(tableData) : void 0;
|
|
4495
|
-
for (const item of table.columns) {
|
|
4496
|
-
const [key, column] = dbColumnToAst(
|
|
4497
|
-
ctx,
|
|
4498
|
-
data,
|
|
4499
|
-
domains,
|
|
4500
|
-
table.name,
|
|
4501
|
-
item,
|
|
4502
|
-
table,
|
|
4503
|
-
tableData,
|
|
4504
|
-
checks
|
|
4505
|
-
);
|
|
4506
|
-
shape[key] = column;
|
|
4507
|
-
}
|
|
4508
|
-
return shape;
|
|
4509
|
-
};
|
|
4510
|
-
const getDbTableColumnsChecks = (tableData) => tableData.constraints.reduce((acc, item) => {
|
|
4511
|
-
var _a;
|
|
4512
|
-
if (isColumnCheck(item)) {
|
|
4513
|
-
(acc[_a = item.check.columns[0]] ?? (acc[_a] = [])).push(item.check.expression);
|
|
4514
|
-
}
|
|
4515
|
-
return acc;
|
|
4516
|
-
}, {});
|
|
4517
|
-
const dbColumnToAst = (ctx, data, domains, tableName, item, table, tableData, checks) => {
|
|
4518
|
-
let column = instantiateDbColumn(ctx, data, domains, item);
|
|
4519
|
-
column.data.name = item.name;
|
|
4520
|
-
if (item.identity) {
|
|
4521
|
-
column.data.identity = item.identity;
|
|
4522
|
-
if (!item.identity.always) delete column.data.identity?.always;
|
|
4523
|
-
}
|
|
4524
|
-
if (tableData?.primaryKey?.columns?.length === 1 && tableData?.primaryKey?.columns[0] === item.name) {
|
|
4525
|
-
column = column.primaryKey();
|
|
4526
|
-
}
|
|
4527
|
-
collectColumnIndexesOrExcludes(item, column, tableName, tableData, "indexes");
|
|
4528
|
-
collectColumnIndexesOrExcludes(
|
|
4529
|
-
item,
|
|
4530
|
-
column,
|
|
4531
|
-
tableName,
|
|
4532
|
-
tableData,
|
|
4533
|
-
"excludes"
|
|
4534
|
-
);
|
|
4535
|
-
if (table) {
|
|
4536
|
-
for (const it of data.constraints) {
|
|
4537
|
-
if (it.tableName !== table.name || it.schemaName !== table.schemaName || it.check || it.references?.columns.length !== 1 || it.references.columns[0] !== item.name || checkIfIsOuterRecursiveFkey(data, table, it.references)) {
|
|
4538
|
-
continue;
|
|
4539
|
-
}
|
|
4540
|
-
const c = dbConstraintToTableConstraint(ctx, table, it);
|
|
4541
|
-
column = column.foreignKey(
|
|
4542
|
-
c.references?.fnOrTable,
|
|
4543
|
-
it.references.foreignColumns[0],
|
|
4544
|
-
c.references?.options
|
|
4545
|
-
);
|
|
4546
|
-
}
|
|
4547
|
-
}
|
|
4548
|
-
const columnChecks = checks?.[item.name];
|
|
4549
|
-
if (columnChecks) {
|
|
4550
|
-
column.data.checks = columnChecks.map((check) => ({
|
|
4551
|
-
sql: new pqb.RawSql([[check]])
|
|
4552
|
-
}));
|
|
4553
|
-
}
|
|
4554
|
-
const camelCaseName = pqb.toCamelCase(item.name);
|
|
4555
|
-
if (ctx.snakeCase) {
|
|
4556
|
-
const snakeCaseName = pqb.toSnakeCase(camelCaseName);
|
|
4557
|
-
if (snakeCaseName !== item.name) column.data.name = item.name;
|
|
4558
|
-
} else if (camelCaseName !== item.name) {
|
|
4559
|
-
column.data.name = item.name;
|
|
4560
|
-
}
|
|
4561
|
-
return [camelCaseName, column];
|
|
4562
|
-
};
|
|
4563
|
-
const collectColumnIndexesOrExcludes = (dbColumn, column, tableName, tableData, key) => {
|
|
4564
|
-
var _a;
|
|
4565
|
-
const items = tableData?.[key];
|
|
4566
|
-
if (!items) return;
|
|
4567
|
-
const columnItems = items.filter(
|
|
4568
|
-
(it) => it.columns.length === 1 && "column" in it.columns[0] && it.columns[0].column === dbColumn.name
|
|
4569
|
-
);
|
|
4570
|
-
for (const item of columnItems) {
|
|
4571
|
-
const columnOptions = item.columns[0];
|
|
4572
|
-
const { name, ...itemOptions } = makeIndexOrExcludeOptions(
|
|
4573
|
-
tableName,
|
|
4574
|
-
item,
|
|
4575
|
-
key
|
|
4576
|
-
);
|
|
4577
|
-
((_a = column.data)[key] ?? (_a[key] = [])).push({
|
|
4578
|
-
with: "exclude" in item && item.exclude ? item.exclude[0] : void 0,
|
|
4579
|
-
options: {
|
|
4580
|
-
name,
|
|
4581
|
-
collate: columnOptions.collate,
|
|
4582
|
-
opclass: columnOptions.opclass,
|
|
4583
|
-
order: columnOptions.order,
|
|
4584
|
-
...itemOptions
|
|
4585
|
-
}
|
|
4586
|
-
});
|
|
4587
|
-
}
|
|
4588
|
-
};
|
|
4589
|
-
const dbConstraintToTableConstraint = (ctx, table, item) => {
|
|
4590
|
-
const { references, check } = item;
|
|
4591
|
-
const constraint = {
|
|
4592
|
-
references: references ? {
|
|
4593
|
-
columns: references.columns,
|
|
4594
|
-
fnOrTable: getReferencesTable(ctx, references),
|
|
4595
|
-
foreignColumns: references.foreignColumns,
|
|
4596
|
-
options: {
|
|
4597
|
-
match: matchMap[references.match],
|
|
4598
|
-
onUpdate: fkeyActionMap[references.onUpdate],
|
|
4599
|
-
onDelete: fkeyActionMap[references.onDelete]
|
|
4600
|
-
}
|
|
4601
|
-
} : void 0,
|
|
4602
|
-
check: check ? pqb.raw({ raw: check.expression }) : void 0
|
|
4603
|
-
};
|
|
4604
|
-
const name = item.name && item.name !== getConstraintName(table.name, constraint, ctx.snakeCase) ? item.name : void 0;
|
|
4605
|
-
if (name) {
|
|
4606
|
-
constraint.name = name;
|
|
4607
|
-
if (constraint.references?.options) {
|
|
4608
|
-
constraint.references.options.name = name;
|
|
4609
|
-
}
|
|
4610
|
-
}
|
|
4611
|
-
return constraint;
|
|
4612
|
-
};
|
|
4613
|
-
const makeIndexOrExcludeOptions = (tableName, index, key) => {
|
|
4614
|
-
return {
|
|
4615
|
-
name: index.name !== (key === "indexes" ? getIndexName : getExcludeName)(
|
|
4616
|
-
tableName,
|
|
4617
|
-
index.columns
|
|
4618
|
-
) ? index.name : void 0,
|
|
4619
|
-
using: index.using === "btree" ? void 0 : index.using,
|
|
4620
|
-
unique: index.unique || void 0,
|
|
4621
|
-
include: index.include,
|
|
4622
|
-
nullsNotDistinct: index.nullsNotDistinct || void 0,
|
|
4623
|
-
with: index.with,
|
|
4624
|
-
tablespace: index.tablespace,
|
|
4625
|
-
where: index.where
|
|
4626
|
-
};
|
|
4627
|
-
};
|
|
4628
|
-
const checkIfIsOuterRecursiveFkey = (data, table, references) => {
|
|
4629
|
-
const referencesId = `${references.foreignSchema}.${references.foreignTable}`;
|
|
4630
|
-
const tableId = `${table.schemaName}.${table.name}`;
|
|
4631
|
-
for (const other of data.tables) {
|
|
4632
|
-
const id = `${other.schemaName}.${other.name}`;
|
|
4633
|
-
if (referencesId === id) {
|
|
4634
|
-
for (const c of data.constraints) {
|
|
4635
|
-
if (c.tableName === other.name && c.schemaName === other.schemaName && c.references?.foreignTable === table.name && c.references.foreignSchema === table.schemaName && tableId < id) {
|
|
4636
|
-
return true;
|
|
4637
|
-
}
|
|
4638
|
-
}
|
|
4639
|
-
break;
|
|
4640
|
-
}
|
|
4641
|
-
}
|
|
4642
|
-
return false;
|
|
4643
|
-
};
|
|
4644
|
-
|
|
4645
3709
|
const astToGenerateItems = (config, asts, currentSchema) => {
|
|
4646
3710
|
return asts.map((ast) => astToGenerateItem(config, ast, currentSchema));
|
|
4647
3711
|
};
|
|
@@ -5027,23 +4091,79 @@ const astEncoders = {
|
|
|
5027
4091
|
const inner = [`${quoteSchemaTable(ast)},`];
|
|
5028
4092
|
code.push(inner);
|
|
5029
4093
|
code = inner;
|
|
5030
|
-
if (hasOptions) {
|
|
5031
|
-
const options = [];
|
|
5032
|
-
if (ast.comment)
|
|
5033
|
-
options.push(`comment: ${JSON.stringify(ast.comment)},`);
|
|
5034
|
-
if (ast.noPrimaryKey === "ignore") options.push(`noPrimaryKey: true,`);
|
|
5035
|
-
code.push("{", options, "},");
|
|
5036
|
-
}
|
|
5037
|
-
code.push("(t) => ({");
|
|
4094
|
+
if (hasOptions) {
|
|
4095
|
+
const options = [];
|
|
4096
|
+
if (ast.comment)
|
|
4097
|
+
options.push(`comment: ${JSON.stringify(ast.comment)},`);
|
|
4098
|
+
if (ast.noPrimaryKey === "ignore") options.push(`noPrimaryKey: true,`);
|
|
4099
|
+
code.push("{", options, "},");
|
|
4100
|
+
}
|
|
4101
|
+
code.push("(t) => ({");
|
|
4102
|
+
} else {
|
|
4103
|
+
pqb.addCode(
|
|
4104
|
+
code,
|
|
4105
|
+
`await db.${ast.action}Table(${quoteSchemaTable(ast)}, (t) => ({`
|
|
4106
|
+
);
|
|
4107
|
+
}
|
|
4108
|
+
const timestamps = getHasTimestamps(
|
|
4109
|
+
ast.shape.createdAt,
|
|
4110
|
+
ast.shape.updatedAt
|
|
4111
|
+
);
|
|
4112
|
+
const toCodeCtx = {
|
|
4113
|
+
t: "t",
|
|
4114
|
+
table: ast.name,
|
|
4115
|
+
currentSchema,
|
|
4116
|
+
migration: true,
|
|
4117
|
+
snakeCase: config.snakeCase
|
|
4118
|
+
};
|
|
4119
|
+
for (const key in ast.shape) {
|
|
4120
|
+
if (timestamps.hasAnyTimestamps && (key === "createdAt" || key === "updatedAt"))
|
|
4121
|
+
continue;
|
|
4122
|
+
const line = [`${pqb.quoteObjectKey(key, config.snakeCase)}: `];
|
|
4123
|
+
const columnCode = ast.shape[key].toCode(toCodeCtx, key);
|
|
4124
|
+
for (const part of columnCode) {
|
|
4125
|
+
pqb.addCode(line, part);
|
|
4126
|
+
}
|
|
4127
|
+
pqb.addCode(line, ",");
|
|
4128
|
+
code.push(line);
|
|
4129
|
+
}
|
|
4130
|
+
if (timestamps.hasAnyTimestamps) {
|
|
4131
|
+
code.push([`...${timestampsToCode(timestamps)},`]);
|
|
4132
|
+
}
|
|
4133
|
+
if (isShifted) {
|
|
4134
|
+
pqb.addCode(code, "}),");
|
|
4135
|
+
if (hasTableData) pqb.pushTableDataCode(code, ast);
|
|
4136
|
+
pqb.addCode(result, ");");
|
|
4137
|
+
} else {
|
|
4138
|
+
pqb.addCode(result, "}));");
|
|
4139
|
+
}
|
|
4140
|
+
return result;
|
|
4141
|
+
},
|
|
4142
|
+
changeTable(ast, config, currentSchema) {
|
|
4143
|
+
let code = [];
|
|
4144
|
+
const result = code;
|
|
4145
|
+
const schemaTable = quoteSchemaTable({
|
|
4146
|
+
schema: ast.schema === currentSchema ? void 0 : ast.schema,
|
|
4147
|
+
name: ast.name
|
|
4148
|
+
});
|
|
4149
|
+
const { comment } = ast;
|
|
4150
|
+
if (comment !== void 0) {
|
|
4151
|
+
pqb.addCode(code, `await db.changeTable(`);
|
|
4152
|
+
const inner = [
|
|
4153
|
+
`${schemaTable},`,
|
|
4154
|
+
`{ comment: ${JSON.stringify(ast.comment)} },`,
|
|
4155
|
+
"(t) => ({"
|
|
4156
|
+
];
|
|
4157
|
+
code.push(inner);
|
|
4158
|
+
code = inner;
|
|
5038
4159
|
} else {
|
|
5039
|
-
pqb.addCode(
|
|
5040
|
-
code,
|
|
5041
|
-
`await db.${ast.action}Table(${quoteSchemaTable(ast)}, (t) => ({`
|
|
5042
|
-
);
|
|
4160
|
+
pqb.addCode(code, `await db.changeTable(${schemaTable}, (t) => ({`);
|
|
5043
4161
|
}
|
|
5044
|
-
const
|
|
5045
|
-
|
|
5046
|
-
|
|
4162
|
+
const [addTimestamps, dropTimestamps] = ["add", "drop"].map(
|
|
4163
|
+
(type) => getHasTimestamps(
|
|
4164
|
+
ast.shape.createdAt && "type" in ast.shape.createdAt && ast.shape.createdAt?.type === type ? ast.shape.createdAt.item : void 0,
|
|
4165
|
+
ast.shape.updatedAt && "type" in ast.shape.updatedAt && ast.shape.updatedAt?.type === type ? ast.shape.updatedAt.item : void 0
|
|
4166
|
+
)
|
|
5047
4167
|
);
|
|
5048
4168
|
const toCodeCtx = {
|
|
5049
4169
|
t: "t",
|
|
@@ -5053,349 +4173,1251 @@ const astEncoders = {
|
|
|
5053
4173
|
snakeCase: config.snakeCase
|
|
5054
4174
|
};
|
|
5055
4175
|
for (const key in ast.shape) {
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
4176
|
+
const changes = pqb.toArray(ast.shape[key]);
|
|
4177
|
+
for (const change of changes) {
|
|
4178
|
+
if (change.type === "add" || change.type === "drop") {
|
|
4179
|
+
if ((addTimestamps.hasAnyTimestamps || dropTimestamps.hasAnyTimestamps) && (key === "createdAt" || key === "updatedAt"))
|
|
4180
|
+
continue;
|
|
4181
|
+
const recreate = changes.length > 1;
|
|
4182
|
+
const line = [
|
|
4183
|
+
recreate ? `...t.${change.type}(t.name(${pqb.singleQuote(
|
|
4184
|
+
change.item.data.name ?? key
|
|
4185
|
+
)})` : `${pqb.quoteObjectKey(key, config.snakeCase)}: t.${change.type}(`
|
|
4186
|
+
];
|
|
4187
|
+
const columnCode = change.item.toCode(toCodeCtx, key);
|
|
4188
|
+
for (let i = 0; i < columnCode.length; i++) {
|
|
4189
|
+
let part = columnCode[i];
|
|
4190
|
+
if (recreate && !i) part = part.slice(1);
|
|
4191
|
+
pqb.addCode(line, part);
|
|
4192
|
+
}
|
|
4193
|
+
pqb.addCode(line, "),");
|
|
4194
|
+
code.push(line);
|
|
4195
|
+
} else if (change.type === "change") {
|
|
4196
|
+
if (!change.from.column || !change.to.column) continue;
|
|
4197
|
+
const line = [
|
|
4198
|
+
`${pqb.quoteObjectKey(key, config.snakeCase)}: t${change.name ? `.name(${pqb.singleQuote(change.name)})` : ""}.change(`
|
|
4199
|
+
];
|
|
4200
|
+
const fromCode = change.from.column.toCode(
|
|
4201
|
+
{
|
|
4202
|
+
t: "t",
|
|
4203
|
+
table: ast.name,
|
|
4204
|
+
currentSchema,
|
|
4205
|
+
migration: true,
|
|
4206
|
+
snakeCase: config.snakeCase
|
|
4207
|
+
},
|
|
4208
|
+
key
|
|
4209
|
+
);
|
|
4210
|
+
for (const part of fromCode) {
|
|
4211
|
+
pqb.addCode(line, part);
|
|
4212
|
+
}
|
|
4213
|
+
pqb.addCode(line, ", ");
|
|
4214
|
+
const toCode = change.to.column.toCode(toCodeCtx, key);
|
|
4215
|
+
for (const part of toCode) {
|
|
4216
|
+
pqb.addCode(line, part);
|
|
4217
|
+
}
|
|
4218
|
+
if (change.using) {
|
|
4219
|
+
pqb.addCode(line, ", {");
|
|
4220
|
+
const u = [];
|
|
4221
|
+
if (change.using.usingUp) {
|
|
4222
|
+
u.push(`usingUp: ${pqb.rawSqlToCode(change.using.usingUp, "t")},`);
|
|
4223
|
+
}
|
|
4224
|
+
if (change.using.usingDown) {
|
|
4225
|
+
u.push(
|
|
4226
|
+
`usingDown: ${pqb.rawSqlToCode(change.using.usingDown, "t")},`
|
|
4227
|
+
);
|
|
4228
|
+
}
|
|
4229
|
+
pqb.addCode(line, u);
|
|
4230
|
+
pqb.addCode(line, "}");
|
|
4231
|
+
}
|
|
4232
|
+
pqb.addCode(line, "),");
|
|
4233
|
+
code.push(line);
|
|
4234
|
+
} else if (change.type === "rename") {
|
|
4235
|
+
code.push([
|
|
4236
|
+
`${pqb.quoteObjectKey(key, config.snakeCase)}: t.rename(${pqb.singleQuote(
|
|
4237
|
+
change.name
|
|
4238
|
+
)}),`
|
|
4239
|
+
]);
|
|
4240
|
+
} else {
|
|
4241
|
+
pqb.exhaustive(change.type);
|
|
4242
|
+
}
|
|
5062
4243
|
}
|
|
5063
|
-
pqb.addCode(line, ",");
|
|
5064
|
-
code.push(line);
|
|
5065
4244
|
}
|
|
5066
|
-
|
|
5067
|
-
|
|
4245
|
+
for (const key of ["drop", "add"]) {
|
|
4246
|
+
const timestamps = key === "add" ? addTimestamps : dropTimestamps;
|
|
4247
|
+
if (timestamps.hasAnyTimestamps) {
|
|
4248
|
+
pqb.addCode(code, [`...t.${key}(${timestampsToCode(timestamps)}),`]);
|
|
4249
|
+
}
|
|
4250
|
+
const { primaryKey, indexes, excludes, constraints } = ast[key];
|
|
4251
|
+
if (primaryKey) {
|
|
4252
|
+
pqb.addCode(code, [
|
|
4253
|
+
`...t.${key}(${pqb.primaryKeyInnerToCode(primaryKey, "t")}),`
|
|
4254
|
+
]);
|
|
4255
|
+
}
|
|
4256
|
+
if (indexes) {
|
|
4257
|
+
for (const item of indexes) {
|
|
4258
|
+
pqb.addCode(code, [`...t.${key}(`, pqb.indexInnerToCode(item, "t"), "),"]);
|
|
4259
|
+
}
|
|
4260
|
+
}
|
|
4261
|
+
if (excludes) {
|
|
4262
|
+
for (const item of excludes) {
|
|
4263
|
+
pqb.addCode(code, [`...t.${key}(`, pqb.excludeInnerToCode(item, "t"), "),"]);
|
|
4264
|
+
}
|
|
4265
|
+
}
|
|
4266
|
+
if (constraints) {
|
|
4267
|
+
for (const item of constraints) {
|
|
4268
|
+
pqb.addCode(code, [
|
|
4269
|
+
`...t.${key}(`,
|
|
4270
|
+
pqb.constraintInnerToCode(item, "t", true),
|
|
4271
|
+
"),"
|
|
4272
|
+
]);
|
|
4273
|
+
}
|
|
4274
|
+
}
|
|
5068
4275
|
}
|
|
5069
|
-
if (
|
|
4276
|
+
if (ast.comment !== void 0) {
|
|
5070
4277
|
pqb.addCode(code, "}),");
|
|
5071
|
-
if (hasTableData) pqb.pushTableDataCode(code, ast);
|
|
5072
4278
|
pqb.addCode(result, ");");
|
|
5073
4279
|
} else {
|
|
5074
4280
|
pqb.addCode(result, "}));");
|
|
5075
4281
|
}
|
|
5076
|
-
return result;
|
|
5077
|
-
},
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
const
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
4282
|
+
return result;
|
|
4283
|
+
},
|
|
4284
|
+
renameType(ast, _, currentSchema) {
|
|
4285
|
+
const code = [];
|
|
4286
|
+
const kind = ast.kind === "TABLE" ? "Table" : "Type";
|
|
4287
|
+
if (ast.from === ast.to) {
|
|
4288
|
+
pqb.addCode(
|
|
4289
|
+
code,
|
|
4290
|
+
`await db.change${kind}Schema(${pqb.singleQuote(ast.to)}, ${pqb.singleQuote(
|
|
4291
|
+
ast.fromSchema ?? currentSchema
|
|
4292
|
+
)}, ${pqb.singleQuote(ast.toSchema ?? currentSchema)});`
|
|
4293
|
+
);
|
|
4294
|
+
} else {
|
|
4295
|
+
pqb.addCode(
|
|
4296
|
+
code,
|
|
4297
|
+
`await db.rename${kind}(${quoteSchemaTable({
|
|
4298
|
+
schema: ast.fromSchema === currentSchema ? void 0 : ast.fromSchema,
|
|
4299
|
+
name: ast.from
|
|
4300
|
+
})}, ${quoteSchemaTable({
|
|
4301
|
+
schema: ast.toSchema === currentSchema ? void 0 : ast.toSchema,
|
|
4302
|
+
name: ast.to
|
|
4303
|
+
})});`
|
|
4304
|
+
);
|
|
4305
|
+
}
|
|
4306
|
+
return code;
|
|
4307
|
+
},
|
|
4308
|
+
schema(ast) {
|
|
4309
|
+
return `await db.${ast.action === "create" ? "createSchema" : "dropSchema"}(${pqb.singleQuote(ast.name)});`;
|
|
4310
|
+
},
|
|
4311
|
+
renameSchema(ast) {
|
|
4312
|
+
return `await db.renameSchema(${pqb.singleQuote(ast.from)}, ${pqb.singleQuote(
|
|
4313
|
+
ast.to
|
|
4314
|
+
)});`;
|
|
4315
|
+
},
|
|
4316
|
+
extension(ast) {
|
|
4317
|
+
const code = [
|
|
4318
|
+
`await db.${ast.action}Extension(${quoteSchemaTable(ast)}`
|
|
4319
|
+
];
|
|
4320
|
+
if (ast.version) {
|
|
4321
|
+
pqb.addCode(code, ", {");
|
|
4322
|
+
code.push([`version: ${pqb.singleQuote(ast.version)},`], "}");
|
|
4323
|
+
}
|
|
4324
|
+
pqb.addCode(code, ");");
|
|
4325
|
+
return code;
|
|
4326
|
+
},
|
|
4327
|
+
enum(ast, _, currentSchema) {
|
|
4328
|
+
return `await db.${ast.action === "create" ? "createEnum" : "dropEnum"}(${quoteSchemaTable(ast, currentSchema)}, [${ast.values.map(pqb.singleQuote).join(", ")}]);`;
|
|
4329
|
+
},
|
|
4330
|
+
enumValues(ast, _, currentSchema) {
|
|
4331
|
+
return `await db.${ast.action}EnumValues(${quoteSchemaTable(
|
|
4332
|
+
ast,
|
|
4333
|
+
currentSchema
|
|
4334
|
+
)}, [${ast.values.map(pqb.singleQuote).join(", ")}]);`;
|
|
4335
|
+
},
|
|
4336
|
+
renameEnumValues(ast, config, currentSchema) {
|
|
4337
|
+
return `await db.renameEnumValues(${quoteSchemaTable(
|
|
4338
|
+
ast,
|
|
4339
|
+
currentSchema
|
|
4340
|
+
)}, { ${Object.entries(ast.values).map(
|
|
4341
|
+
([from, to]) => `${pqb.quoteObjectKey(from, config.snakeCase)}: ${pqb.singleQuote(to)}`
|
|
4342
|
+
).join(", ")} });`;
|
|
4343
|
+
},
|
|
4344
|
+
changeEnumValues(ast, _, currentSchema) {
|
|
4345
|
+
return `await db.changeEnumValues(${quoteSchemaTable(
|
|
4346
|
+
ast,
|
|
4347
|
+
currentSchema
|
|
4348
|
+
)}, [${ast.fromValues.map(pqb.singleQuote).join(", ")}], [${ast.toValues.map(pqb.singleQuote).join(", ")}]);`;
|
|
4349
|
+
},
|
|
4350
|
+
domain(ast, _, currentSchema) {
|
|
4351
|
+
return `await db.${ast.action}Domain(${quoteSchemaTable(
|
|
4352
|
+
ast
|
|
4353
|
+
)}, (t) => ${ast.baseType.toCode(
|
|
4354
|
+
{ t: "t", table: ast.name, currentSchema },
|
|
4355
|
+
ast.baseType.data.name ?? ""
|
|
4356
|
+
)});`;
|
|
4357
|
+
},
|
|
4358
|
+
collation(ast) {
|
|
4359
|
+
const params = [];
|
|
4360
|
+
if (ast.locale) params.push(`locale: '${ast.locale}',`);
|
|
4361
|
+
if (ast.lcCollate) params.push(`lcCollate: '${ast.lcCollate}',`);
|
|
4362
|
+
if (ast.lcCType) params.push(`lcCType: '${ast.lcCType}',`);
|
|
4363
|
+
if (ast.provider) params.push(`provider: '${ast.provider}',`);
|
|
4364
|
+
if (ast.deterministic) params.push(`deterministic: ${ast.deterministic},`);
|
|
4365
|
+
if (ast.version) params.push(`version: '${ast.version}',`);
|
|
4366
|
+
return [
|
|
4367
|
+
`await db.createCollation(${quoteSchemaTable(ast)}, {`,
|
|
4368
|
+
params,
|
|
4369
|
+
"});"
|
|
4370
|
+
];
|
|
4371
|
+
},
|
|
4372
|
+
constraint(ast) {
|
|
4373
|
+
const table = quoteSchemaTable({
|
|
4374
|
+
schema: ast.tableSchema,
|
|
4375
|
+
name: ast.tableName
|
|
4376
|
+
});
|
|
4377
|
+
if (ast.references) {
|
|
4378
|
+
return [
|
|
4379
|
+
`await db.addForeignKey(`,
|
|
4380
|
+
[`${table},`, ...pqb.referencesArgsToCode(ast.references, ast.name, true)],
|
|
4381
|
+
");"
|
|
4382
|
+
];
|
|
4383
|
+
}
|
|
4384
|
+
const check = ast.check;
|
|
4385
|
+
return [
|
|
4386
|
+
`await db.addCheck(${table}, ${pqb.rawSqlToCode(check, "t")}${ast.name ? `, ${pqb.singleQuote(ast.name)}` : ""});`
|
|
4387
|
+
];
|
|
4388
|
+
},
|
|
4389
|
+
renameTableItem(ast) {
|
|
4390
|
+
return [
|
|
4391
|
+
`await db.rename${ast.kind === "INDEX" ? "Index" : "Constraint"}(${quoteSchemaTable({
|
|
4392
|
+
schema: ast.tableSchema,
|
|
4393
|
+
name: ast.tableName
|
|
4394
|
+
})}, ${pqb.singleQuote(ast.from)}, ${pqb.singleQuote(ast.to)});`
|
|
4395
|
+
];
|
|
4396
|
+
},
|
|
4397
|
+
view(ast) {
|
|
4398
|
+
const code = [`await db.createView(${quoteSchemaTable(ast)}`];
|
|
4399
|
+
const options = [];
|
|
4400
|
+
if (ast.options.recursive) options.push("recursive: true,");
|
|
4401
|
+
const w = ast.options.with;
|
|
4402
|
+
if (w?.checkOption) options.push(`checkOption: '${w.checkOption}',`);
|
|
4403
|
+
if (w?.securityBarrier)
|
|
4404
|
+
options.push(`securityBarrier: ${w.securityBarrier},`);
|
|
4405
|
+
if (w?.securityInvoker)
|
|
4406
|
+
options.push(`securityInvoker: ${w.securityInvoker},`);
|
|
4407
|
+
if (options.length) {
|
|
4408
|
+
pqb.addCode(code, ", {");
|
|
4409
|
+
code.push(options, "}");
|
|
4410
|
+
}
|
|
4411
|
+
pqb.addCode(code, ", ");
|
|
4412
|
+
if (!ast.sql._values) {
|
|
4413
|
+
const raw = ast.sql._sql;
|
|
4414
|
+
let sql;
|
|
4415
|
+
if (typeof raw === "string") {
|
|
4416
|
+
sql = raw;
|
|
4417
|
+
} else {
|
|
4418
|
+
sql = "";
|
|
4419
|
+
const parts = raw[0];
|
|
4420
|
+
const last = parts.length - 1;
|
|
4421
|
+
for (let i = 0; i < last; i++) {
|
|
4422
|
+
sql += parts[i] + `\${${raw[i + 1]}}`;
|
|
4423
|
+
}
|
|
4424
|
+
sql += parts[last];
|
|
4425
|
+
}
|
|
4426
|
+
pqb.addCode(code, pqb.backtickQuote(sql));
|
|
5095
4427
|
} else {
|
|
5096
|
-
pqb.addCode(code,
|
|
4428
|
+
pqb.addCode(code, pqb.rawSqlToCode(ast.sql, "db"));
|
|
5097
4429
|
}
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
4430
|
+
pqb.addCode(code, ");");
|
|
4431
|
+
return code;
|
|
4432
|
+
}
|
|
4433
|
+
};
|
|
4434
|
+
const isTimestamp = (column, type) => {
|
|
4435
|
+
if (!column) return false;
|
|
4436
|
+
const { default: def } = column.data;
|
|
4437
|
+
return Boolean(
|
|
4438
|
+
column instanceof type && !column.data.isNullable && def && typeof def === "object" && pqb.isRawSQL(def) && (typeof def._sql === "object" ? def._sql[0][0] : def._sql) === "now()"
|
|
4439
|
+
);
|
|
4440
|
+
};
|
|
4441
|
+
const getHasTimestamps = (createdAt, updatedAt) => {
|
|
4442
|
+
const timestamps = getTimestampsInfo(createdAt, updatedAt, pqb.TimestampTZColumn);
|
|
4443
|
+
const timestampsNoTZ = getTimestampsInfo(
|
|
4444
|
+
createdAt,
|
|
4445
|
+
updatedAt,
|
|
4446
|
+
pqb.TimestampColumn
|
|
4447
|
+
);
|
|
4448
|
+
return {
|
|
4449
|
+
hasTZTimestamps: timestamps,
|
|
4450
|
+
hasAnyTimestamps: timestamps || timestampsNoTZ
|
|
4451
|
+
};
|
|
4452
|
+
};
|
|
4453
|
+
const getTimestampsInfo = (createdAt, updatedAt, type) => {
|
|
4454
|
+
return isTimestamp(createdAt, type) && isTimestamp(updatedAt, type) && (!createdAt?.data.name || createdAt?.data.name === "created_at") && (!updatedAt?.data.name || updatedAt?.data.name === "updated_at");
|
|
4455
|
+
};
|
|
4456
|
+
const timestampsToCode = ({ hasTZTimestamps }) => {
|
|
4457
|
+
const key = hasTZTimestamps ? "timestamps" : "timestampsNoTZ";
|
|
4458
|
+
return `t.${key}()`;
|
|
4459
|
+
};
|
|
4460
|
+
|
|
4461
|
+
const filterSchema = (table) => `${table} !~ '^pg_' AND ${table} != 'information_schema'`;
|
|
4462
|
+
const jsonAgg = (sql2, as) => `(SELECT coalesce(json_agg(t.*), '[]') FROM (${sql2}) t) AS "${as}"`;
|
|
4463
|
+
const columnsSql = ({
|
|
4464
|
+
schema,
|
|
4465
|
+
table,
|
|
4466
|
+
join = "",
|
|
4467
|
+
where
|
|
4468
|
+
}) => `SELECT
|
|
4469
|
+
${schema}.nspname "schemaName",
|
|
4470
|
+
${table}.relname "tableName",
|
|
4471
|
+
a.attname "name",
|
|
4472
|
+
t.typname "type",
|
|
4473
|
+
tn.nspname "typeSchema",
|
|
4474
|
+
a.attndims "arrayDims",
|
|
4475
|
+
information_schema._pg_char_max_length(tt.id, tt.mod) "maxChars",
|
|
4476
|
+
information_schema._pg_numeric_precision(tt.id, tt.mod) "numericPrecision",
|
|
4477
|
+
information_schema._pg_numeric_scale(tt.id,tt.mod) "numericScale",
|
|
4478
|
+
information_schema._pg_datetime_precision(tt.id,tt.mod) "dateTimePrecision",
|
|
4479
|
+
CAST(
|
|
4480
|
+
CASE WHEN a.attgenerated = ''
|
|
4481
|
+
THEN pg_get_expr(ad.adbin, ad.adrelid)
|
|
4482
|
+
END AS information_schema.character_data
|
|
4483
|
+
) AS "default",
|
|
4484
|
+
NOT (a.attnotnull OR (t.typtype = 'd' AND t.typnotnull)) AS "isNullable",
|
|
4485
|
+
co.collname AS "collate",
|
|
4486
|
+
NULLIF(a.attcompression, '') AS compression,
|
|
4487
|
+
pgd.description AS "comment",
|
|
4488
|
+
(
|
|
4489
|
+
CASE WHEN a.attidentity IN ('a', 'd') THEN (
|
|
4490
|
+
json_build_object(
|
|
4491
|
+
'always',
|
|
4492
|
+
a.attidentity = 'a',
|
|
4493
|
+
'start',
|
|
4494
|
+
seq.seqstart,
|
|
4495
|
+
'increment',
|
|
4496
|
+
seq.seqincrement,
|
|
4497
|
+
'min',
|
|
4498
|
+
nullif(seq.seqmin, 1),
|
|
4499
|
+
'max',
|
|
4500
|
+
nullif(seq.seqmax, (
|
|
4501
|
+
CASE t.typname
|
|
4502
|
+
WHEN 'int2' THEN 32767
|
|
4503
|
+
WHEN 'int4' THEN 2147483647
|
|
4504
|
+
WHEN 'int8' THEN 9223372036854775807
|
|
4505
|
+
ELSE NULL
|
|
4506
|
+
END
|
|
4507
|
+
)),
|
|
4508
|
+
'cache',
|
|
4509
|
+
seq.seqcache,
|
|
4510
|
+
'cycle',
|
|
4511
|
+
seq.seqcycle
|
|
4512
|
+
)
|
|
4513
|
+
) END
|
|
4514
|
+
) "identity",
|
|
4515
|
+
ext.extname "extension",
|
|
4516
|
+
a.atttypmod "typmod"
|
|
4517
|
+
FROM pg_attribute a
|
|
4518
|
+
${join}
|
|
4519
|
+
LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum
|
|
4520
|
+
JOIN pg_type t
|
|
4521
|
+
ON t.oid = (
|
|
4522
|
+
CASE WHEN a.attndims = 0
|
|
4523
|
+
THEN a.atttypid
|
|
4524
|
+
ELSE (SELECT t.typelem FROM pg_type t WHERE t.oid = a.atttypid)
|
|
4525
|
+
END
|
|
4526
|
+
)
|
|
4527
|
+
JOIN LATERAL (
|
|
4528
|
+
SELECT
|
|
4529
|
+
CASE WHEN t.typtype = 'd' THEN t.typbasetype ELSE t.oid END id,
|
|
4530
|
+
CASE WHEN t.typtype = 'd' THEN t.typtypmod ELSE a.atttypmod END mod
|
|
4531
|
+
) tt ON true
|
|
4532
|
+
JOIN pg_namespace tn ON tn.oid = t.typnamespace
|
|
4533
|
+
LEFT JOIN (pg_collation co JOIN pg_namespace nco ON (co.collnamespace = nco.oid))
|
|
4534
|
+
ON a.attcollation = co.oid AND (nco.nspname, co.collname) <> ('pg_catalog', 'default')
|
|
4535
|
+
LEFT JOIN pg_catalog.pg_description pgd
|
|
4536
|
+
ON pgd.objoid = a.attrelid
|
|
4537
|
+
AND pgd.objsubid = a.attnum
|
|
4538
|
+
LEFT JOIN (pg_depend dep JOIN pg_sequence seq ON (dep.classid = 'pg_class'::regclass AND dep.objid = seq.seqrelid AND dep.deptype = 'i'))
|
|
4539
|
+
ON (dep.refclassid = 'pg_class'::regclass AND dep.refobjid = ${table}.oid AND dep.refobjsubid = a.attnum)
|
|
4540
|
+
LEFT JOIN pg_depend d ON d.objid = t.oid AND d.classid = 'pg_type'::regclass AND d.deptype = 'e'
|
|
4541
|
+
LEFT JOIN pg_extension ext ON ext.oid = d.refobjid
|
|
4542
|
+
WHERE a.attnum > 0
|
|
4543
|
+
AND NOT a.attisdropped
|
|
4544
|
+
AND ${where}
|
|
4545
|
+
ORDER BY a.attnum`;
|
|
4546
|
+
const schemasSql = `SELECT coalesce(json_agg(nspname ORDER BY nspname), '[]')
|
|
4547
|
+
FROM pg_catalog.pg_namespace n
|
|
4548
|
+
WHERE ${filterSchema("nspname")}`;
|
|
4549
|
+
const tablesSql = `SELECT
|
|
4550
|
+
nspname AS "schemaName",
|
|
4551
|
+
relname AS "name",
|
|
4552
|
+
obj_description(c.oid) AS comment,
|
|
4553
|
+
(SELECT coalesce(json_agg(t), '[]') FROM (${columnsSql({
|
|
4554
|
+
schema: "n",
|
|
4555
|
+
table: "c",
|
|
4556
|
+
where: "a.attrelid = c.oid"
|
|
4557
|
+
})}) t) AS "columns"
|
|
4558
|
+
FROM pg_class c
|
|
4559
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = relnamespace
|
|
4560
|
+
WHERE (relkind = 'r' OR relkind = 'p')
|
|
4561
|
+
AND ${filterSchema("nspname")}
|
|
4562
|
+
ORDER BY relname`;
|
|
4563
|
+
const viewsSql = `SELECT
|
|
4564
|
+
nc.nspname AS "schemaName",
|
|
4565
|
+
c.relname AS "name",
|
|
4566
|
+
(
|
|
4567
|
+
SELECT COALESCE(json_agg(t.*), '[]')
|
|
4568
|
+
FROM (
|
|
4569
|
+
SELECT
|
|
4570
|
+
ns.nspname AS "schemaName",
|
|
4571
|
+
obj.relname AS "name"
|
|
4572
|
+
FROM pg_class obj
|
|
4573
|
+
JOIN pg_depend dep ON dep.refobjid = obj.oid
|
|
4574
|
+
JOIN pg_rewrite rew ON rew.oid = dep.objid
|
|
4575
|
+
JOIN pg_namespace ns ON ns.oid = obj.relnamespace
|
|
4576
|
+
WHERE rew.ev_class = c.oid AND obj.oid <> c.oid
|
|
4577
|
+
) t
|
|
4578
|
+
) "deps",
|
|
4579
|
+
right(substring(r.ev_action from ':hasRecursive \\w'), 1)::bool AS "isRecursive",
|
|
4580
|
+
array_to_json(c.reloptions) AS "with",
|
|
4581
|
+
(SELECT coalesce(json_agg(t), '[]') FROM (${columnsSql({
|
|
4582
|
+
schema: "nc",
|
|
4583
|
+
table: "c",
|
|
4584
|
+
where: "a.attrelid = c.oid"
|
|
4585
|
+
})}) t) AS "columns",
|
|
4586
|
+
pg_get_viewdef(c.oid) AS "sql"
|
|
4587
|
+
FROM pg_namespace nc
|
|
4588
|
+
JOIN pg_class c
|
|
4589
|
+
ON nc.oid = c.relnamespace
|
|
4590
|
+
AND c.relkind = 'v'
|
|
4591
|
+
AND c.relpersistence != 't'
|
|
4592
|
+
JOIN pg_rewrite r ON r.ev_class = c.oid
|
|
4593
|
+
WHERE ${filterSchema("nc.nspname")}
|
|
4594
|
+
ORDER BY c.relname`;
|
|
4595
|
+
const indexesSql = `SELECT
|
|
4596
|
+
n.nspname "schemaName",
|
|
4597
|
+
t.relname "tableName",
|
|
4598
|
+
ic.relname "name",
|
|
4599
|
+
am.amname AS "using",
|
|
4600
|
+
i.indisunique "unique",
|
|
4601
|
+
(
|
|
4602
|
+
SELECT json_agg(
|
|
4603
|
+
(
|
|
4604
|
+
CASE WHEN t.e = 0
|
|
4605
|
+
THEN jsonb_build_object('expression', pg_get_indexdef(i.indexrelid, t.i::int4, false))
|
|
4606
|
+
ELSE jsonb_build_object('column', (
|
|
4607
|
+
(
|
|
4608
|
+
SELECT attname
|
|
4609
|
+
FROM pg_catalog.pg_attribute
|
|
4610
|
+
WHERE attrelid = i.indrelid
|
|
4611
|
+
AND attnum = t.e
|
|
4612
|
+
)
|
|
4613
|
+
))
|
|
4614
|
+
END
|
|
4615
|
+
) || (
|
|
4616
|
+
CASE WHEN i.indcollation[t.i - 1] = 0
|
|
4617
|
+
THEN '{}'::jsonb
|
|
4618
|
+
ELSE (
|
|
4619
|
+
SELECT (
|
|
4620
|
+
CASE WHEN collname = 'default'
|
|
4621
|
+
THEN '{}'::jsonb
|
|
4622
|
+
ELSE jsonb_build_object('collate', collname)
|
|
4623
|
+
END
|
|
4624
|
+
)
|
|
4625
|
+
FROM pg_catalog.pg_collation
|
|
4626
|
+
WHERE oid = i.indcollation[t.i - 1]
|
|
4627
|
+
)
|
|
4628
|
+
END
|
|
4629
|
+
) || (
|
|
4630
|
+
SELECT
|
|
4631
|
+
CASE WHEN opcdefault AND attoptions IS NULL
|
|
4632
|
+
THEN '{}'::jsonb
|
|
4633
|
+
ELSE jsonb_build_object(
|
|
4634
|
+
'opclass', opcname || COALESCE('(' || array_to_string(attoptions, ', ') || ')', '')
|
|
4635
|
+
)
|
|
4636
|
+
END
|
|
4637
|
+
FROM pg_opclass
|
|
4638
|
+
LEFT JOIN pg_attribute
|
|
4639
|
+
ON attrelid = i.indexrelid
|
|
4640
|
+
AND attnum = t.i
|
|
4641
|
+
WHERE oid = i.indclass[t.i - 1]
|
|
4642
|
+
) || (
|
|
4643
|
+
CASE WHEN i.indoption[t.i - 1] = 0
|
|
4644
|
+
THEN '{}'::jsonb
|
|
4645
|
+
ELSE jsonb_build_object(
|
|
4646
|
+
'order',
|
|
4647
|
+
CASE
|
|
4648
|
+
WHEN i.indoption[t.i - 1] = 1 THEN 'DESC NULLS LAST'
|
|
4649
|
+
WHEN i.indoption[t.i - 1] = 2 THEN 'ASC NULLS FIRST'
|
|
4650
|
+
WHEN i.indoption[t.i - 1] = 3 THEN 'DESC'
|
|
4651
|
+
ELSE NULL
|
|
4652
|
+
END
|
|
4653
|
+
)
|
|
4654
|
+
END
|
|
5102
4655
|
)
|
|
5103
|
-
)
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
4656
|
+
)
|
|
4657
|
+
FROM unnest(i.indkey[:indnkeyatts - 1]) WITH ORDINALITY AS t(e, i)
|
|
4658
|
+
) "columns",
|
|
4659
|
+
(
|
|
4660
|
+
SELECT json_agg(
|
|
4661
|
+
(
|
|
4662
|
+
SELECT attname
|
|
4663
|
+
FROM pg_catalog.pg_attribute
|
|
4664
|
+
WHERE attrelid = i.indrelid
|
|
4665
|
+
AND attnum = j.e
|
|
4666
|
+
)
|
|
4667
|
+
)
|
|
4668
|
+
FROM unnest(i.indkey[indnkeyatts:]) AS j(e)
|
|
4669
|
+
) AS "include",
|
|
4670
|
+
(to_jsonb(i.*)->'indnullsnotdistinct')::bool AS "nullsNotDistinct",
|
|
4671
|
+
NULLIF(pg_catalog.array_to_string(
|
|
4672
|
+
ic.reloptions || array(SELECT 'toast.' || x FROM pg_catalog.unnest(tc.reloptions) x),
|
|
4673
|
+
', '
|
|
4674
|
+
), '') AS "with",
|
|
4675
|
+
(
|
|
4676
|
+
SELECT tablespace
|
|
4677
|
+
FROM pg_indexes
|
|
4678
|
+
WHERE schemaname = n.nspname
|
|
4679
|
+
AND indexname = ic.relname
|
|
4680
|
+
) AS tablespace,
|
|
4681
|
+
pg_get_expr(i.indpred, i.indrelid) AS "where",
|
|
4682
|
+
(
|
|
4683
|
+
CASE i.indisexclusion WHEN true
|
|
4684
|
+
THEN (
|
|
4685
|
+
SELECT json_agg(o.oprname)
|
|
4686
|
+
FROM pg_catalog.pg_constraint c, LATERAL unnest(c.conexclop) op_oid
|
|
4687
|
+
JOIN pg_operator o ON o.oid = op_oid
|
|
4688
|
+
WHERE c.conindid = ic.oid
|
|
4689
|
+
)
|
|
4690
|
+
END
|
|
4691
|
+
) "exclude"
|
|
4692
|
+
FROM pg_index i
|
|
4693
|
+
JOIN pg_class t ON t.oid = i.indrelid
|
|
4694
|
+
JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
4695
|
+
JOIN pg_class ic ON ic.oid = i.indexrelid
|
|
4696
|
+
JOIN pg_am am ON am.oid = ic.relam
|
|
4697
|
+
LEFT JOIN pg_catalog.pg_class tc ON (ic.reltoastrelid = tc.oid)
|
|
4698
|
+
WHERE ${filterSchema("n.nspname")}
|
|
4699
|
+
AND NOT i.indisprimary
|
|
4700
|
+
ORDER BY ic.relname`;
|
|
4701
|
+
const constraintsSql = `SELECT
|
|
4702
|
+
s.nspname AS "schemaName",
|
|
4703
|
+
t.relname AS "tableName",
|
|
4704
|
+
c.conname AS "name",
|
|
4705
|
+
(
|
|
4706
|
+
SELECT json_agg(ccu.column_name)
|
|
4707
|
+
FROM information_schema.constraint_column_usage ccu
|
|
4708
|
+
WHERE contype = 'p'
|
|
4709
|
+
AND ccu.constraint_name = c.conname
|
|
4710
|
+
AND ccu.table_schema = s.nspname
|
|
4711
|
+
) AS "primaryKey",
|
|
4712
|
+
(
|
|
4713
|
+
SELECT
|
|
4714
|
+
json_build_object(
|
|
4715
|
+
'foreignSchema',
|
|
4716
|
+
fs.nspname,
|
|
4717
|
+
'foreignTable',
|
|
4718
|
+
ft.relname,
|
|
4719
|
+
'columns',
|
|
4720
|
+
(
|
|
4721
|
+
SELECT json_agg(ccu.column_name)
|
|
4722
|
+
FROM information_schema.key_column_usage ccu
|
|
4723
|
+
WHERE ccu.constraint_name = c.conname
|
|
4724
|
+
AND ccu.table_schema = cs.nspname
|
|
4725
|
+
),
|
|
4726
|
+
'foreignColumns',
|
|
4727
|
+
(
|
|
4728
|
+
SELECT json_agg(ccu.column_name)
|
|
4729
|
+
FROM information_schema.constraint_column_usage ccu
|
|
4730
|
+
WHERE ccu.constraint_name = c.conname
|
|
4731
|
+
AND ccu.table_schema = cs.nspname
|
|
4732
|
+
),
|
|
4733
|
+
'match',
|
|
4734
|
+
c.confmatchtype,
|
|
4735
|
+
'onUpdate',
|
|
4736
|
+
c.confupdtype,
|
|
4737
|
+
'onDelete',
|
|
4738
|
+
c.confdeltype
|
|
4739
|
+
)
|
|
4740
|
+
FROM pg_class ft
|
|
4741
|
+
JOIN pg_catalog.pg_namespace fs ON fs.oid = ft.relnamespace
|
|
4742
|
+
JOIN pg_catalog.pg_namespace cs ON cs.oid = c.connamespace
|
|
4743
|
+
WHERE contype = 'f' AND ft.oid = confrelid
|
|
4744
|
+
) AS "references",
|
|
4745
|
+
(
|
|
4746
|
+
SELECT
|
|
4747
|
+
CASE conbin IS NULL
|
|
4748
|
+
WHEN false THEN
|
|
4749
|
+
json_build_object(
|
|
4750
|
+
'columns',
|
|
4751
|
+
json_agg(ccu.column_name),
|
|
4752
|
+
'expression',
|
|
4753
|
+
pg_get_expr(conbin, conrelid)
|
|
4754
|
+
)
|
|
4755
|
+
END
|
|
4756
|
+
FROM information_schema.constraint_column_usage ccu
|
|
4757
|
+
WHERE conbin IS NOT NULL
|
|
4758
|
+
AND ccu.constraint_name = c.conname
|
|
4759
|
+
AND ccu.table_schema = s.nspname
|
|
4760
|
+
) AS "check"
|
|
4761
|
+
FROM pg_catalog.pg_constraint c
|
|
4762
|
+
JOIN pg_class t ON t.oid = conrelid
|
|
4763
|
+
JOIN pg_catalog.pg_namespace s
|
|
4764
|
+
ON s.oid = t.relnamespace
|
|
4765
|
+
AND contype IN ('p', 'f', 'c')
|
|
4766
|
+
AND ${filterSchema("s.nspname")}
|
|
4767
|
+
ORDER BY c.conname`;
|
|
4768
|
+
const triggersSql = `SELECT event_object_schema AS "schemaName",
|
|
4769
|
+
event_object_table AS "tableName",
|
|
4770
|
+
trigger_schema AS "triggerSchema",
|
|
4771
|
+
trigger_name AS name,
|
|
4772
|
+
json_agg(event_manipulation) AS events,
|
|
4773
|
+
action_timing AS activation,
|
|
4774
|
+
action_condition AS condition,
|
|
4775
|
+
action_statement AS definition
|
|
4776
|
+
FROM information_schema.triggers
|
|
4777
|
+
WHERE ${filterSchema("event_object_schema")}
|
|
4778
|
+
GROUP BY event_object_schema, event_object_table, trigger_schema, trigger_name, action_timing, action_condition, action_statement
|
|
4779
|
+
ORDER BY trigger_name`;
|
|
4780
|
+
const extensionsSql = `SELECT
|
|
4781
|
+
nspname AS "schemaName",
|
|
4782
|
+
extname AS "name",
|
|
4783
|
+
extversion AS version
|
|
4784
|
+
FROM pg_extension
|
|
4785
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = extnamespace
|
|
4786
|
+
AND ${filterSchema("n.nspname")}`;
|
|
4787
|
+
const enumsSql = `SELECT
|
|
4788
|
+
n.nspname as "schemaName",
|
|
4789
|
+
t.typname as name,
|
|
4790
|
+
json_agg(e.enumlabel ORDER BY e.enumsortorder) as values
|
|
4791
|
+
FROM pg_type t
|
|
4792
|
+
JOIN pg_enum e ON t.oid = e.enumtypid
|
|
4793
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
4794
|
+
WHERE ${filterSchema("n.nspname")}
|
|
4795
|
+
GROUP BY n.nspname, t.typname`;
|
|
4796
|
+
const domainsSql = `SELECT
|
|
4797
|
+
n.nspname AS "schemaName",
|
|
4798
|
+
d.typname AS "name",
|
|
4799
|
+
t.typname AS "type",
|
|
4800
|
+
s.nspname AS "typeSchema",
|
|
4801
|
+
NOT d.typnotnull AS "isNullable",
|
|
4802
|
+
d.typndims AS "arrayDims",
|
|
4803
|
+
character_maximum_length AS "maxChars",
|
|
4804
|
+
numeric_precision AS "numericPrecision",
|
|
4805
|
+
numeric_scale AS "numericScale",
|
|
4806
|
+
datetime_precision AS "dateTimePrecision",
|
|
4807
|
+
collation_name AS "collate",
|
|
4808
|
+
domain_default AS "default",
|
|
4809
|
+
(
|
|
4810
|
+
SELECT json_agg(pg_get_expr(conbin, conrelid))
|
|
4811
|
+
FROM pg_catalog.pg_constraint c
|
|
4812
|
+
WHERE c.contypid = d.oid
|
|
4813
|
+
) AS "checks"
|
|
4814
|
+
FROM pg_catalog.pg_type d
|
|
4815
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = d.typnamespace
|
|
4816
|
+
JOIN information_schema.domains i
|
|
4817
|
+
ON i.domain_schema = nspname
|
|
4818
|
+
AND i.domain_name = d.typname
|
|
4819
|
+
JOIN pg_catalog.pg_type t
|
|
4820
|
+
ON (
|
|
4821
|
+
CASE WHEN d.typcategory = 'A'
|
|
4822
|
+
THEN t.typarray
|
|
4823
|
+
ELSE t.oid
|
|
4824
|
+
END
|
|
4825
|
+
) = d.typbasetype
|
|
4826
|
+
JOIN pg_catalog.pg_namespace s ON s.oid = t.typnamespace
|
|
4827
|
+
WHERE d.typtype = 'd' AND ${filterSchema("n.nspname")}`;
|
|
4828
|
+
const collationsSql = (version) => `SELECT
|
|
4829
|
+
nspname "schemaName",
|
|
4830
|
+
collname "name",
|
|
4831
|
+
CASE WHEN collprovider = 'i' THEN 'icu' WHEN collprovider = 'c' THEN 'libc' ELSE collprovider::text END "provider",
|
|
4832
|
+
collisdeterministic "deterministic",
|
|
4833
|
+
collcollate "lcCollate",
|
|
4834
|
+
collctype "lcCType",
|
|
4835
|
+
${version >= 17 ? "colllocale" : "colliculocale"} "locale",
|
|
4836
|
+
collversion "version"
|
|
4837
|
+
FROM pg_collation
|
|
4838
|
+
JOIN pg_namespace n on pg_collation.collnamespace = n.oid
|
|
4839
|
+
WHERE ${filterSchema("n.nspname")}`;
|
|
4840
|
+
const sql = (version) => `SELECT (${schemasSql}) AS "schemas", ${jsonAgg(
|
|
4841
|
+
tablesSql,
|
|
4842
|
+
"tables"
|
|
4843
|
+
)}, ${jsonAgg(viewsSql, "views")}, ${jsonAgg(
|
|
4844
|
+
indexesSql,
|
|
4845
|
+
"indexes"
|
|
4846
|
+
)}, ${jsonAgg(constraintsSql, "constraints")}, ${jsonAgg(
|
|
4847
|
+
triggersSql,
|
|
4848
|
+
"triggers"
|
|
4849
|
+
)}, ${jsonAgg(extensionsSql, "extensions")}, ${jsonAgg(
|
|
4850
|
+
enumsSql,
|
|
4851
|
+
"enums"
|
|
4852
|
+
)}, ${jsonAgg(domainsSql, "domains")}, ${jsonAgg(
|
|
4853
|
+
collationsSql(version),
|
|
4854
|
+
"collations"
|
|
4855
|
+
)}`;
|
|
4856
|
+
async function introspectDbSchema(db) {
|
|
4857
|
+
const {
|
|
4858
|
+
rows: [{ version: versionString }]
|
|
4859
|
+
} = await db.query("SELECT version()");
|
|
4860
|
+
const version = +versionString.match(/\d+/)[0];
|
|
4861
|
+
const data = await db.query(sql(version));
|
|
4862
|
+
const result = data.rows[0];
|
|
4863
|
+
for (const domain of result.domains) {
|
|
4864
|
+
domain.checks = domain.checks?.filter((check) => check);
|
|
4865
|
+
nullsToUndefined(domain);
|
|
4866
|
+
}
|
|
4867
|
+
for (const table of result.tables) {
|
|
4868
|
+
for (const column of table.columns) {
|
|
4869
|
+
nullsToUndefined(column);
|
|
4870
|
+
if (column.identity) nullsToUndefined(column.identity);
|
|
4871
|
+
if (column.compression) {
|
|
4872
|
+
column.compression = column.compression === "p" ? "pglz" : "lz4";
|
|
5179
4873
|
}
|
|
5180
4874
|
}
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
if (
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
}
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
4875
|
+
}
|
|
4876
|
+
const indexes = [];
|
|
4877
|
+
const excludes = [];
|
|
4878
|
+
for (const index of result.indexes) {
|
|
4879
|
+
nullsToUndefined(index);
|
|
4880
|
+
for (const column of index.columns) {
|
|
4881
|
+
if (!("expression" in column)) continue;
|
|
4882
|
+
const s = column.expression;
|
|
4883
|
+
const columnR = `"?\\w+"?`;
|
|
4884
|
+
const langR = `(${columnR}|'\\w+'::regconfig)`;
|
|
4885
|
+
const firstColumnR = `[(]*${columnR}`;
|
|
4886
|
+
const concatR = `\\|\\|`;
|
|
4887
|
+
const restColumnR = ` ${concatR} ' '::text\\) ${concatR} ${columnR}\\)`;
|
|
4888
|
+
const coalesceColumn = `COALESCE\\(${columnR}, ''::text\\)`;
|
|
4889
|
+
const tsVectorR = `to_tsvector\\(${langR}, (${firstColumnR}|${restColumnR}|${coalesceColumn})+\\)`;
|
|
4890
|
+
const weightR = `'\\w'::"char"`;
|
|
4891
|
+
const setWeightR = `setweight\\(${tsVectorR}, ${weightR}\\)`;
|
|
4892
|
+
const setWeightOrTsVectorR = `(${setWeightR}|${tsVectorR})`;
|
|
4893
|
+
const match = s.match(
|
|
4894
|
+
new RegExp(`^([\\(]*${setWeightOrTsVectorR}[\\)]*( ${concatR} )?)+$`)
|
|
4895
|
+
);
|
|
4896
|
+
if (!match) continue;
|
|
4897
|
+
let language;
|
|
4898
|
+
let languageColumn;
|
|
4899
|
+
const tokens = match[0].match(
|
|
4900
|
+
new RegExp(
|
|
4901
|
+
`setweight\\(|to_tsvector\\(${langR}|[:']?${columnR}\\(?`,
|
|
4902
|
+
"g"
|
|
4903
|
+
)
|
|
4904
|
+
)?.reduce((acc, token) => {
|
|
4905
|
+
if (token === "setweight(" || token === "COALESCE(" || token[0] === ":")
|
|
4906
|
+
return acc;
|
|
4907
|
+
if (token.startsWith("to_tsvector(")) {
|
|
4908
|
+
if (token[12] === "'") {
|
|
4909
|
+
language = token.slice(13, -12);
|
|
4910
|
+
} else {
|
|
4911
|
+
languageColumn = token.slice(12);
|
|
4912
|
+
}
|
|
4913
|
+
} else if (token[0] === "'") {
|
|
4914
|
+
acc.push({ kind: "weight", value: token[1] });
|
|
4915
|
+
} else {
|
|
4916
|
+
if (token[0] === '"') token = token.slice(1, -1);
|
|
4917
|
+
acc.push({ kind: "column", value: token });
|
|
5195
4918
|
}
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
4919
|
+
return acc;
|
|
4920
|
+
}, []);
|
|
4921
|
+
if (!tokens) continue;
|
|
4922
|
+
index.language = language;
|
|
4923
|
+
index.languageColumn = languageColumn;
|
|
4924
|
+
index.tsVector = true;
|
|
4925
|
+
index.columns = [];
|
|
4926
|
+
for (const token of tokens) {
|
|
4927
|
+
if (token.kind === "column") {
|
|
4928
|
+
index.columns.push({
|
|
4929
|
+
column: token.value
|
|
4930
|
+
});
|
|
4931
|
+
} else if (token.kind === "weight") {
|
|
4932
|
+
index.columns[index.columns.length - 1].weight = token.value;
|
|
5200
4933
|
}
|
|
5201
4934
|
}
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
4935
|
+
}
|
|
4936
|
+
(index.exclude ? excludes : indexes).push(index);
|
|
4937
|
+
}
|
|
4938
|
+
result.indexes = indexes;
|
|
4939
|
+
result.excludes = excludes;
|
|
4940
|
+
return result;
|
|
4941
|
+
}
|
|
4942
|
+
const nullsToUndefined = (obj) => {
|
|
4943
|
+
for (const key in obj) {
|
|
4944
|
+
if (obj[key] === null)
|
|
4945
|
+
obj[key] = void 0;
|
|
4946
|
+
}
|
|
4947
|
+
};
|
|
4948
|
+
|
|
4949
|
+
const matchMap = {
|
|
4950
|
+
s: void 0,
|
|
4951
|
+
f: "FULL",
|
|
4952
|
+
p: "PARTIAL"
|
|
4953
|
+
};
|
|
4954
|
+
const fkeyActionMap = {
|
|
4955
|
+
a: void 0,
|
|
4956
|
+
// default
|
|
4957
|
+
r: "RESTRICT",
|
|
4958
|
+
c: "CASCADE",
|
|
4959
|
+
n: "SET NULL",
|
|
4960
|
+
d: "SET DEFAULT"
|
|
4961
|
+
};
|
|
4962
|
+
const makeStructureToAstCtx = (config, currentSchema) => ({
|
|
4963
|
+
snakeCase: config.snakeCase,
|
|
4964
|
+
unsupportedTypes: {},
|
|
4965
|
+
currentSchema,
|
|
4966
|
+
columnSchemaConfig: config.schemaConfig,
|
|
4967
|
+
columnsByType: pqb.makeColumnsByType(config.schemaConfig)
|
|
4968
|
+
});
|
|
4969
|
+
const structureToAst = async (ctx, adapter, config) => {
|
|
4970
|
+
const ast = [];
|
|
4971
|
+
const data = await introspectDbSchema(adapter);
|
|
4972
|
+
for (const name of data.schemas) {
|
|
4973
|
+
if (name === "public") continue;
|
|
4974
|
+
ast.push({
|
|
4975
|
+
type: "schema",
|
|
4976
|
+
action: "create",
|
|
4977
|
+
name
|
|
4978
|
+
});
|
|
4979
|
+
}
|
|
4980
|
+
for (const it of data.collations) {
|
|
4981
|
+
ast.push({
|
|
4982
|
+
type: "collation",
|
|
4983
|
+
action: "create",
|
|
4984
|
+
...it,
|
|
4985
|
+
schema: it.schemaName === ctx.currentSchema ? void 0 : it.schemaName
|
|
4986
|
+
});
|
|
4987
|
+
}
|
|
4988
|
+
const domains = makeDomainsMap(ctx, data);
|
|
4989
|
+
const { schema: migrationsSchema = "public", table: migrationsTable } = getMigrationsSchemaAndTable(config);
|
|
4990
|
+
for (const table of data.tables) {
|
|
4991
|
+
if (table.name === migrationsTable && table.schemaName === migrationsSchema)
|
|
4992
|
+
continue;
|
|
4993
|
+
ast.push(tableToAst(ctx, data, table, "create", domains));
|
|
4994
|
+
}
|
|
4995
|
+
for (const it of data.extensions) {
|
|
4996
|
+
ast.push({
|
|
4997
|
+
type: "extension",
|
|
4998
|
+
action: "create",
|
|
4999
|
+
name: it.name,
|
|
5000
|
+
schema: it.schemaName === ctx.currentSchema ? void 0 : it.schemaName,
|
|
5001
|
+
version: it.version
|
|
5002
|
+
});
|
|
5003
|
+
}
|
|
5004
|
+
for (const it of data.enums) {
|
|
5005
|
+
ast.push({
|
|
5006
|
+
type: "enum",
|
|
5007
|
+
action: "create",
|
|
5008
|
+
name: it.name,
|
|
5009
|
+
schema: it.schemaName === ctx.currentSchema ? void 0 : it.schemaName,
|
|
5010
|
+
values: it.values
|
|
5011
|
+
});
|
|
5012
|
+
}
|
|
5013
|
+
for (const it of data.domains) {
|
|
5014
|
+
ast.push({
|
|
5015
|
+
type: "domain",
|
|
5016
|
+
action: "create",
|
|
5017
|
+
schema: it.schemaName === ctx.currentSchema ? void 0 : it.schemaName,
|
|
5018
|
+
name: it.name,
|
|
5019
|
+
baseType: domains[`${it.schemaName}.${it.name}`]
|
|
5020
|
+
});
|
|
5021
|
+
}
|
|
5022
|
+
for (const table of data.tables) {
|
|
5023
|
+
for (const fkey of data.constraints) {
|
|
5024
|
+
if (fkey.references && fkey.tableName === table.name && fkey.schemaName === table.schemaName && checkIfIsOuterRecursiveFkey(data, table, fkey.references)) {
|
|
5025
|
+
ast.push({
|
|
5026
|
+
...constraintToAst(ctx, fkey),
|
|
5027
|
+
type: "constraint",
|
|
5028
|
+
action: "create",
|
|
5029
|
+
tableSchema: table.schemaName === ctx.currentSchema ? void 0 : table.schemaName,
|
|
5030
|
+
tableName: fkey.tableName
|
|
5031
|
+
});
|
|
5210
5032
|
}
|
|
5211
5033
|
}
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5034
|
+
}
|
|
5035
|
+
for (const view of data.views) {
|
|
5036
|
+
ast.push(viewToAst(ctx, data, domains, view));
|
|
5037
|
+
}
|
|
5038
|
+
return ast;
|
|
5039
|
+
};
|
|
5040
|
+
const makeDomainsMap = (ctx, data) => {
|
|
5041
|
+
const domains = {};
|
|
5042
|
+
for (const it of data.domains) {
|
|
5043
|
+
const column = instantiateDbColumn(ctx, data, domains, {
|
|
5044
|
+
schemaName: it.schemaName,
|
|
5045
|
+
name: it.name,
|
|
5046
|
+
type: it.type,
|
|
5047
|
+
typeSchema: it.typeSchema,
|
|
5048
|
+
arrayDims: it.arrayDims,
|
|
5049
|
+
tableName: "",
|
|
5050
|
+
isNullable: it.isNullable,
|
|
5051
|
+
collate: it.collate,
|
|
5052
|
+
default: it.default,
|
|
5053
|
+
typmod: -1
|
|
5054
|
+
});
|
|
5055
|
+
if (it.checks) {
|
|
5056
|
+
column.data.checks = it.checks.map((check) => ({
|
|
5057
|
+
sql: new pqb.RawSql([[check]])
|
|
5058
|
+
}));
|
|
5059
|
+
}
|
|
5060
|
+
domains[`${it.schemaName}.${it.name}`] = column;
|
|
5061
|
+
}
|
|
5062
|
+
return domains;
|
|
5063
|
+
};
|
|
5064
|
+
const getDbColumnIsSerial = (item) => {
|
|
5065
|
+
if (item.type === "int2" || item.type === "int4" || item.type === "int8") {
|
|
5066
|
+
const { default: def, schemaName, tableName, name } = item;
|
|
5067
|
+
const seq = `${tableName}_${name}_seq`;
|
|
5068
|
+
if (def && (def === `nextval(${pqb.singleQuote(`${seq}`)}::regclass)` || def === `nextval(${pqb.singleQuote(`"${seq}"`)}::regclass)` || def === `nextval(${pqb.singleQuote(`${schemaName}.${seq}`)}::regclass)` || def === `nextval(${pqb.singleQuote(`"${schemaName}".${seq}`)}::regclass)` || def === `nextval(${pqb.singleQuote(`${schemaName}."${seq}"`)}::regclass)` || def === `nextval(${pqb.singleQuote(`"${schemaName}"."${seq}"`)}::regclass)`)) {
|
|
5069
|
+
return true;
|
|
5217
5070
|
}
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5071
|
+
}
|
|
5072
|
+
return false;
|
|
5073
|
+
};
|
|
5074
|
+
const instantiateDbColumn = (ctx, data, domains, dbColumn) => {
|
|
5075
|
+
var _a, _b;
|
|
5076
|
+
const isSerial = getDbColumnIsSerial(dbColumn);
|
|
5077
|
+
if (isSerial) {
|
|
5078
|
+
dbColumn = { ...dbColumn, default: void 0 };
|
|
5079
|
+
}
|
|
5080
|
+
let column;
|
|
5081
|
+
const col = instantiateColumnByDbType(ctx, dbColumn.type, isSerial, dbColumn);
|
|
5082
|
+
if (col) {
|
|
5083
|
+
column = col;
|
|
5084
|
+
} else {
|
|
5085
|
+
const { typeSchema, type: typeName } = dbColumn;
|
|
5086
|
+
const typeId = typeSchema === "pg_catalog" ? typeName : `${typeSchema}.${typeName}`;
|
|
5087
|
+
const domainColumn = domains[typeId];
|
|
5088
|
+
if (domainColumn) {
|
|
5089
|
+
column = new pqb.DomainColumn(
|
|
5090
|
+
ctx.columnSchemaConfig,
|
|
5091
|
+
typeName,
|
|
5092
|
+
typeSchema,
|
|
5093
|
+
dbColumn.extension
|
|
5094
|
+
).as(domainColumn);
|
|
5230
5095
|
} else {
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
`await db.rename${kind}(${quoteSchemaTable({
|
|
5234
|
-
schema: ast.fromSchema === currentSchema ? void 0 : ast.fromSchema,
|
|
5235
|
-
name: ast.from
|
|
5236
|
-
})}, ${quoteSchemaTable({
|
|
5237
|
-
schema: ast.toSchema === currentSchema ? void 0 : ast.toSchema,
|
|
5238
|
-
name: ast.to
|
|
5239
|
-
})});`
|
|
5096
|
+
const enumType = data.enums.find(
|
|
5097
|
+
(x) => x.name === typeName && x.schemaName === typeSchema
|
|
5240
5098
|
);
|
|
5099
|
+
if (enumType) {
|
|
5100
|
+
column = new pqb.EnumColumn(
|
|
5101
|
+
ctx.columnSchemaConfig,
|
|
5102
|
+
typeSchema === ctx.currentSchema ? typeName : typeId,
|
|
5103
|
+
enumType.values,
|
|
5104
|
+
ctx.columnSchemaConfig.type
|
|
5105
|
+
);
|
|
5106
|
+
} else {
|
|
5107
|
+
column = new pqb.CustomTypeColumn(
|
|
5108
|
+
ctx.columnSchemaConfig,
|
|
5109
|
+
typeName,
|
|
5110
|
+
typeSchema === "pg_catalog" ? void 0 : typeSchema,
|
|
5111
|
+
dbColumn.extension
|
|
5112
|
+
);
|
|
5113
|
+
((_a = ctx.unsupportedTypes)[_b = dbColumn.type] ?? (_a[_b] = [])).push(
|
|
5114
|
+
`${dbColumn.schemaName}${dbColumn.tableName ? `.${dbColumn.tableName}` : ""}.${dbColumn.name}`
|
|
5115
|
+
);
|
|
5116
|
+
}
|
|
5117
|
+
pqb.assignDbDataToColumn(column, dbColumn);
|
|
5241
5118
|
}
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
)
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5119
|
+
}
|
|
5120
|
+
column.data.name = void 0;
|
|
5121
|
+
if (!column.data.isNullable) column.data.isNullable = void 0;
|
|
5122
|
+
if (dbColumn.arrayDims) {
|
|
5123
|
+
const arr = new pqb.ArrayColumn(
|
|
5124
|
+
ctx.columnSchemaConfig,
|
|
5125
|
+
column,
|
|
5126
|
+
ctx.columnSchemaConfig.type
|
|
5127
|
+
);
|
|
5128
|
+
arr.data.isNullable = dbColumn.isNullable;
|
|
5129
|
+
arr.data.arrayDims = dbColumn.arrayDims;
|
|
5130
|
+
column = arr;
|
|
5131
|
+
}
|
|
5132
|
+
return column;
|
|
5133
|
+
};
|
|
5134
|
+
const instantiateColumnByDbType = (ctx, type, isSerial, params) => {
|
|
5135
|
+
let columnFn = ctx.columnsByType[!isSerial ? type : type === "int2" ? "smallserial" : type === "int4" ? "serial" : "bigserial"];
|
|
5136
|
+
if (!columnFn && params.extension === "postgis" && type === "geography" && pqb.PostgisGeographyPointColumn.isDefaultPoint(params.typmod)) {
|
|
5137
|
+
columnFn = ctx.columnsByType.geographyDefaultPoint;
|
|
5138
|
+
}
|
|
5139
|
+
return columnFn ? pqb.assignDbDataToColumn(columnFn(), params) : void 0;
|
|
5140
|
+
};
|
|
5141
|
+
const tableToAst = (ctx, data, table, action, domains) => {
|
|
5142
|
+
const { schemaName, name: tableName } = table;
|
|
5143
|
+
const tableData = getDbStructureTableData(data, table);
|
|
5144
|
+
const { primaryKey, constraints } = tableData;
|
|
5145
|
+
return {
|
|
5146
|
+
type: "table",
|
|
5147
|
+
action,
|
|
5148
|
+
schema: schemaName === ctx.currentSchema ? void 0 : schemaName,
|
|
5149
|
+
comment: table.comment,
|
|
5150
|
+
name: tableName,
|
|
5151
|
+
shape: makeDbStructureColumnsShape(ctx, data, domains, table, tableData),
|
|
5152
|
+
noPrimaryKey: tableData.primaryKey ? "error" : "ignore",
|
|
5153
|
+
primaryKey: primaryKey && primaryKey.columns.length > 1 ? { ...primaryKey, columns: primaryKey.columns.map(pqb.toCamelCase) } : void 0,
|
|
5154
|
+
indexes: indexesOrExcludesToAst(
|
|
5155
|
+
tableName,
|
|
5156
|
+
tableData,
|
|
5157
|
+
"indexes"
|
|
5158
|
+
),
|
|
5159
|
+
excludes: indexesOrExcludesToAst(
|
|
5160
|
+
tableName,
|
|
5161
|
+
tableData,
|
|
5162
|
+
"excludes"
|
|
5163
|
+
),
|
|
5164
|
+
constraints: constraints.reduce((acc, it) => {
|
|
5165
|
+
if (it.check && it.references || it.check && it.check.columns?.length !== 1 || it.references && it.references.columns.length !== 1 && !checkIfIsOuterRecursiveFkey(data, table, it.references)) {
|
|
5166
|
+
acc.push(dbConstraintToTableConstraint(ctx, table, it));
|
|
5167
|
+
}
|
|
5168
|
+
return acc;
|
|
5169
|
+
}, [])
|
|
5170
|
+
};
|
|
5171
|
+
};
|
|
5172
|
+
const indexesOrExcludesToAst = (tableName, tableData, key) => {
|
|
5173
|
+
return tableData[key].reduce((acc, item) => {
|
|
5174
|
+
if (item.columns.length > 1 || item.columns.some((it) => "expression" in it)) {
|
|
5175
|
+
const options = makeIndexOrExcludeOptions(tableName, item, key);
|
|
5176
|
+
acc.push({
|
|
5177
|
+
columns: item.columns.map((it, i) => ({
|
|
5178
|
+
with: "exclude" in item && item.exclude ? item.exclude[i] : void 0,
|
|
5179
|
+
..."expression" in it ? { expression: it.expression } : { column: pqb.toCamelCase(it.column) },
|
|
5180
|
+
collate: it.collate,
|
|
5181
|
+
opclass: it.opclass,
|
|
5182
|
+
order: it.order
|
|
5183
|
+
})),
|
|
5184
|
+
options: {
|
|
5185
|
+
...options,
|
|
5186
|
+
include: item.include?.map(pqb.toCamelCase)
|
|
5187
|
+
}
|
|
5188
|
+
});
|
|
5259
5189
|
}
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
}
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
)
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
if (
|
|
5302
|
-
|
|
5303
|
-
`await db.createCollation(${quoteSchemaTable(ast)}, {`,
|
|
5304
|
-
params,
|
|
5305
|
-
"});"
|
|
5306
|
-
];
|
|
5307
|
-
},
|
|
5308
|
-
constraint(ast) {
|
|
5309
|
-
const table = quoteSchemaTable({
|
|
5310
|
-
schema: ast.tableSchema,
|
|
5311
|
-
name: ast.tableName
|
|
5312
|
-
});
|
|
5313
|
-
if (ast.references) {
|
|
5314
|
-
return [
|
|
5315
|
-
`await db.addForeignKey(`,
|
|
5316
|
-
[`${table},`, ...pqb.referencesArgsToCode(ast.references, ast.name, true)],
|
|
5317
|
-
");"
|
|
5318
|
-
];
|
|
5190
|
+
return acc;
|
|
5191
|
+
}, []);
|
|
5192
|
+
};
|
|
5193
|
+
const getDbStructureTableData = (data, { name, schemaName }) => {
|
|
5194
|
+
const filterFn = filterByTableSchema(name, schemaName);
|
|
5195
|
+
const constraints = data.constraints.filter(filterFn);
|
|
5196
|
+
const primaryKey = constraints.find((c) => c.primaryKey);
|
|
5197
|
+
return {
|
|
5198
|
+
primaryKey: primaryKey?.primaryKey ? {
|
|
5199
|
+
columns: primaryKey.primaryKey,
|
|
5200
|
+
name: primaryKey.name === `${name}_pkey` ? void 0 : primaryKey.name
|
|
5201
|
+
} : void 0,
|
|
5202
|
+
indexes: data.indexes.filter(filterFn),
|
|
5203
|
+
excludes: data.excludes.filter(filterFn),
|
|
5204
|
+
constraints
|
|
5205
|
+
};
|
|
5206
|
+
};
|
|
5207
|
+
const filterByTableSchema = (tableName, schemaName) => (x) => x.tableName === tableName && x.schemaName === schemaName;
|
|
5208
|
+
const constraintToAst = (ctx, item) => {
|
|
5209
|
+
const result = {};
|
|
5210
|
+
const { references, check } = item;
|
|
5211
|
+
if (references) {
|
|
5212
|
+
const options = {};
|
|
5213
|
+
result.references = {
|
|
5214
|
+
columns: references.columns,
|
|
5215
|
+
fnOrTable: getReferencesTable(ctx, references),
|
|
5216
|
+
foreignColumns: references.foreignColumns,
|
|
5217
|
+
options
|
|
5218
|
+
};
|
|
5219
|
+
const match = matchMap[references.match];
|
|
5220
|
+
if (match) options.match = match;
|
|
5221
|
+
const onUpdate = fkeyActionMap[references.onUpdate];
|
|
5222
|
+
if (onUpdate) options.onUpdate = onUpdate;
|
|
5223
|
+
const onDelete = fkeyActionMap[references.onDelete];
|
|
5224
|
+
if (onDelete) options.onDelete = onDelete;
|
|
5225
|
+
}
|
|
5226
|
+
if (check) {
|
|
5227
|
+
result.check = pqb.raw({ raw: check.expression });
|
|
5228
|
+
}
|
|
5229
|
+
if (item.name && item.name !== getConstraintName(item.tableName, result, ctx.snakeCase)) {
|
|
5230
|
+
result.name = item.name;
|
|
5231
|
+
if (result.references?.options) {
|
|
5232
|
+
result.references.options.name = item.name;
|
|
5319
5233
|
}
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
}
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
view
|
|
5334
|
-
const
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
if (w?.securityBarrier)
|
|
5340
|
-
options.push(`securityBarrier: ${w.securityBarrier},`);
|
|
5341
|
-
if (w?.securityInvoker)
|
|
5342
|
-
options.push(`securityInvoker: ${w.securityInvoker},`);
|
|
5343
|
-
if (options.length) {
|
|
5344
|
-
pqb.addCode(code, ", {");
|
|
5345
|
-
code.push(options, "}");
|
|
5234
|
+
}
|
|
5235
|
+
return result;
|
|
5236
|
+
};
|
|
5237
|
+
const getReferencesTable = (ctx, references) => {
|
|
5238
|
+
return references.foreignSchema !== ctx.currentSchema ? `${references.foreignSchema}.${references.foreignTable}` : references.foreignTable;
|
|
5239
|
+
};
|
|
5240
|
+
const isColumnCheck = (it) => {
|
|
5241
|
+
return !it.references && it.check?.columns?.length === 1;
|
|
5242
|
+
};
|
|
5243
|
+
const viewToAst = (ctx, data, domains, view) => {
|
|
5244
|
+
const shape = makeDbStructureColumnsShape(ctx, data, domains, view);
|
|
5245
|
+
const options = {};
|
|
5246
|
+
if (view.isRecursive) options.recursive = true;
|
|
5247
|
+
if (view.with) {
|
|
5248
|
+
const withOptions = {};
|
|
5249
|
+
options.with = withOptions;
|
|
5250
|
+
for (const pair of view.with) {
|
|
5251
|
+
const [key, value] = pair.split("=");
|
|
5252
|
+
withOptions[pqb.toCamelCase(key)] = value === "true" ? true : value === "false" ? false : value;
|
|
5346
5253
|
}
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5254
|
+
}
|
|
5255
|
+
return {
|
|
5256
|
+
type: "view",
|
|
5257
|
+
action: "create",
|
|
5258
|
+
schema: view.schemaName === ctx.currentSchema ? void 0 : view.schemaName,
|
|
5259
|
+
name: view.name,
|
|
5260
|
+
shape,
|
|
5261
|
+
sql: pqb.raw({ raw: view.sql }),
|
|
5262
|
+
options,
|
|
5263
|
+
deps: view.deps
|
|
5264
|
+
};
|
|
5265
|
+
};
|
|
5266
|
+
const makeDbStructureColumnsShape = (ctx, data, domains, table, tableData) => {
|
|
5267
|
+
const shape = {};
|
|
5268
|
+
const checks = tableData ? getDbTableColumnsChecks(tableData) : void 0;
|
|
5269
|
+
for (const item of table.columns) {
|
|
5270
|
+
const [key, column] = dbColumnToAst(
|
|
5271
|
+
ctx,
|
|
5272
|
+
data,
|
|
5273
|
+
domains,
|
|
5274
|
+
table.name,
|
|
5275
|
+
item,
|
|
5276
|
+
table,
|
|
5277
|
+
tableData,
|
|
5278
|
+
checks
|
|
5279
|
+
);
|
|
5280
|
+
shape[key] = column;
|
|
5281
|
+
}
|
|
5282
|
+
return shape;
|
|
5283
|
+
};
|
|
5284
|
+
const getDbTableColumnsChecks = (tableData) => tableData.constraints.reduce((acc, item) => {
|
|
5285
|
+
var _a;
|
|
5286
|
+
if (isColumnCheck(item)) {
|
|
5287
|
+
(acc[_a = item.check.columns[0]] ?? (acc[_a] = [])).push(item.check.expression);
|
|
5288
|
+
}
|
|
5289
|
+
return acc;
|
|
5290
|
+
}, {});
|
|
5291
|
+
const dbColumnToAst = (ctx, data, domains, tableName, item, table, tableData, checks) => {
|
|
5292
|
+
let column = instantiateDbColumn(ctx, data, domains, item);
|
|
5293
|
+
column.data.name = item.name;
|
|
5294
|
+
if (item.identity) {
|
|
5295
|
+
column.data.identity = item.identity;
|
|
5296
|
+
if (!item.identity.always) delete column.data.identity?.always;
|
|
5297
|
+
}
|
|
5298
|
+
if (tableData?.primaryKey?.columns?.length === 1 && tableData?.primaryKey?.columns[0] === item.name) {
|
|
5299
|
+
column = column.primaryKey();
|
|
5300
|
+
}
|
|
5301
|
+
collectColumnIndexesOrExcludes(item, column, tableName, tableData, "indexes");
|
|
5302
|
+
collectColumnIndexesOrExcludes(
|
|
5303
|
+
item,
|
|
5304
|
+
column,
|
|
5305
|
+
tableName,
|
|
5306
|
+
tableData,
|
|
5307
|
+
"excludes"
|
|
5308
|
+
);
|
|
5309
|
+
if (table) {
|
|
5310
|
+
for (const it of data.constraints) {
|
|
5311
|
+
if (it.tableName !== table.name || it.schemaName !== table.schemaName || it.check || it.references?.columns.length !== 1 || it.references.columns[0] !== item.name || checkIfIsOuterRecursiveFkey(data, table, it.references)) {
|
|
5312
|
+
continue;
|
|
5361
5313
|
}
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5314
|
+
const c = dbConstraintToTableConstraint(ctx, table, it);
|
|
5315
|
+
column = column.foreignKey(
|
|
5316
|
+
c.references?.fnOrTable,
|
|
5317
|
+
it.references.foreignColumns[0],
|
|
5318
|
+
c.references?.options
|
|
5319
|
+
);
|
|
5365
5320
|
}
|
|
5366
|
-
pqb.addCode(code, ");");
|
|
5367
|
-
return code;
|
|
5368
5321
|
}
|
|
5322
|
+
const columnChecks = checks?.[item.name];
|
|
5323
|
+
if (columnChecks) {
|
|
5324
|
+
column.data.checks = columnChecks.map((check) => ({
|
|
5325
|
+
sql: new pqb.RawSql([[check]])
|
|
5326
|
+
}));
|
|
5327
|
+
}
|
|
5328
|
+
const camelCaseName = pqb.toCamelCase(item.name);
|
|
5329
|
+
if (ctx.snakeCase) {
|
|
5330
|
+
const snakeCaseName = pqb.toSnakeCase(camelCaseName);
|
|
5331
|
+
if (snakeCaseName !== item.name) column.data.name = item.name;
|
|
5332
|
+
} else if (camelCaseName !== item.name) {
|
|
5333
|
+
column.data.name = item.name;
|
|
5334
|
+
}
|
|
5335
|
+
return [camelCaseName, column];
|
|
5369
5336
|
};
|
|
5370
|
-
const
|
|
5371
|
-
|
|
5372
|
-
const
|
|
5373
|
-
|
|
5374
|
-
|
|
5337
|
+
const collectColumnIndexesOrExcludes = (dbColumn, column, tableName, tableData, key) => {
|
|
5338
|
+
var _a;
|
|
5339
|
+
const items = tableData?.[key];
|
|
5340
|
+
if (!items) return;
|
|
5341
|
+
const columnItems = items.filter(
|
|
5342
|
+
(it) => it.columns.length === 1 && "column" in it.columns[0] && it.columns[0].column === dbColumn.name
|
|
5375
5343
|
);
|
|
5344
|
+
for (const item of columnItems) {
|
|
5345
|
+
const columnOptions = item.columns[0];
|
|
5346
|
+
const { name, ...itemOptions } = makeIndexOrExcludeOptions(
|
|
5347
|
+
tableName,
|
|
5348
|
+
item,
|
|
5349
|
+
key
|
|
5350
|
+
);
|
|
5351
|
+
((_a = column.data)[key] ?? (_a[key] = [])).push({
|
|
5352
|
+
with: "exclude" in item && item.exclude ? item.exclude[0] : void 0,
|
|
5353
|
+
options: {
|
|
5354
|
+
name,
|
|
5355
|
+
collate: columnOptions.collate,
|
|
5356
|
+
opclass: columnOptions.opclass,
|
|
5357
|
+
order: columnOptions.order,
|
|
5358
|
+
...itemOptions
|
|
5359
|
+
}
|
|
5360
|
+
});
|
|
5361
|
+
}
|
|
5376
5362
|
};
|
|
5377
|
-
const
|
|
5378
|
-
const
|
|
5379
|
-
const
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5363
|
+
const dbConstraintToTableConstraint = (ctx, table, item) => {
|
|
5364
|
+
const { references, check } = item;
|
|
5365
|
+
const constraint = {
|
|
5366
|
+
references: references ? {
|
|
5367
|
+
columns: references.columns,
|
|
5368
|
+
fnOrTable: getReferencesTable(ctx, references),
|
|
5369
|
+
foreignColumns: references.foreignColumns,
|
|
5370
|
+
options: {
|
|
5371
|
+
match: matchMap[references.match],
|
|
5372
|
+
onUpdate: fkeyActionMap[references.onUpdate],
|
|
5373
|
+
onDelete: fkeyActionMap[references.onDelete]
|
|
5374
|
+
}
|
|
5375
|
+
} : void 0,
|
|
5376
|
+
check: check ? pqb.raw({ raw: check.expression }) : void 0
|
|
5387
5377
|
};
|
|
5378
|
+
const name = item.name && item.name !== getConstraintName(table.name, constraint, ctx.snakeCase) ? item.name : void 0;
|
|
5379
|
+
if (name) {
|
|
5380
|
+
constraint.name = name;
|
|
5381
|
+
if (constraint.references?.options) {
|
|
5382
|
+
constraint.references.options.name = name;
|
|
5383
|
+
}
|
|
5384
|
+
}
|
|
5385
|
+
return constraint;
|
|
5388
5386
|
};
|
|
5389
|
-
const
|
|
5390
|
-
return
|
|
5387
|
+
const makeIndexOrExcludeOptions = (tableName, index, key) => {
|
|
5388
|
+
return {
|
|
5389
|
+
name: index.name !== (key === "indexes" ? getIndexName : getExcludeName)(
|
|
5390
|
+
tableName,
|
|
5391
|
+
index.columns
|
|
5392
|
+
) ? index.name : void 0,
|
|
5393
|
+
using: index.using === "btree" ? void 0 : index.using,
|
|
5394
|
+
unique: index.unique || void 0,
|
|
5395
|
+
include: index.include,
|
|
5396
|
+
nullsNotDistinct: index.nullsNotDistinct || void 0,
|
|
5397
|
+
with: index.with,
|
|
5398
|
+
tablespace: index.tablespace,
|
|
5399
|
+
where: index.where
|
|
5400
|
+
};
|
|
5391
5401
|
};
|
|
5392
|
-
const
|
|
5393
|
-
const
|
|
5394
|
-
|
|
5402
|
+
const checkIfIsOuterRecursiveFkey = (data, table, references) => {
|
|
5403
|
+
const referencesId = `${references.foreignSchema}.${references.foreignTable}`;
|
|
5404
|
+
const tableId = `${table.schemaName}.${table.name}`;
|
|
5405
|
+
for (const other of data.tables) {
|
|
5406
|
+
const id = `${other.schemaName}.${other.name}`;
|
|
5407
|
+
if (referencesId === id) {
|
|
5408
|
+
for (const c of data.constraints) {
|
|
5409
|
+
if (c.tableName === other.name && c.schemaName === other.schemaName && c.references?.foreignTable === table.name && c.references.foreignSchema === table.schemaName && tableId < id) {
|
|
5410
|
+
return true;
|
|
5411
|
+
}
|
|
5412
|
+
}
|
|
5413
|
+
break;
|
|
5414
|
+
}
|
|
5415
|
+
}
|
|
5416
|
+
return false;
|
|
5395
5417
|
};
|
|
5396
5418
|
|
|
5397
5419
|
const pullDbStructure = async (adapter, config) => {
|
|
5398
|
-
const currentSchema = adapter.
|
|
5420
|
+
const currentSchema = adapter.searchPath || "public";
|
|
5399
5421
|
const ctx = makeStructureToAstCtx(config, currentSchema);
|
|
5400
5422
|
const ast = await structureToAst(ctx, adapter, config);
|
|
5401
5423
|
const result = astToMigration(currentSchema, config, ast);
|
|
@@ -5898,6 +5920,7 @@ exports.getDbStructureTableData = getDbStructureTableData;
|
|
|
5898
5920
|
exports.getDbTableColumnsChecks = getDbTableColumnsChecks;
|
|
5899
5921
|
exports.getExcludeName = getExcludeName;
|
|
5900
5922
|
exports.getIndexName = getIndexName;
|
|
5923
|
+
exports.getMigrationsSchemaAndTable = getMigrationsSchemaAndTable;
|
|
5901
5924
|
exports.getSchemaAndTableFromName = getSchemaAndTableFromName;
|
|
5902
5925
|
exports.instantiateDbColumn = instantiateDbColumn;
|
|
5903
5926
|
exports.introspectDbSchema = introspectDbSchema;
|