relq 1.0.24 → 1.0.26
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/cjs/cli/commands/commit.cjs +80 -0
- package/dist/cjs/cli/commands/import.cjs +1 -0
- package/dist/cjs/cli/commands/pull.cjs +8 -25
- package/dist/cjs/cli/commands/push.cjs +48 -8
- package/dist/cjs/cli/commands/rollback.cjs +205 -84
- package/dist/cjs/cli/commands/schema-ast.cjs +219 -0
- package/dist/cjs/cli/index.cjs +6 -0
- package/dist/cjs/cli/utils/ast-codegen.cjs +95 -3
- package/dist/cjs/cli/utils/ast-transformer.cjs +12 -0
- package/dist/cjs/cli/utils/change-tracker.cjs +135 -0
- package/dist/cjs/cli/utils/commit-manager.cjs +54 -0
- package/dist/cjs/cli/utils/migration-generator.cjs +319 -0
- package/dist/cjs/cli/utils/repo-manager.cjs +99 -3
- package/dist/cjs/cli/utils/schema-diff.cjs +390 -0
- package/dist/cjs/cli/utils/schema-hash.cjs +4 -0
- package/dist/cjs/cli/utils/schema-to-ast.cjs +477 -0
- package/dist/cjs/cli/utils/schema-validator.cjs +21 -1
- package/dist/cjs/schema-definition/column-types.cjs +63 -10
- package/dist/cjs/schema-definition/pg-enum.cjs +10 -0
- package/dist/cjs/schema-definition/pg-function.cjs +19 -0
- package/dist/cjs/schema-definition/pg-sequence.cjs +22 -1
- package/dist/cjs/schema-definition/pg-trigger.cjs +39 -0
- package/dist/cjs/schema-definition/pg-view.cjs +17 -0
- package/dist/cjs/schema-definition/sql-expressions.cjs +3 -0
- package/dist/cjs/schema-definition/table-definition.cjs +4 -0
- package/dist/config.d.ts +98 -0
- package/dist/esm/cli/commands/commit.js +83 -3
- package/dist/esm/cli/commands/import.js +1 -0
- package/dist/esm/cli/commands/pull.js +9 -26
- package/dist/esm/cli/commands/push.js +49 -9
- package/dist/esm/cli/commands/rollback.js +206 -85
- package/dist/esm/cli/commands/schema-ast.js +183 -0
- package/dist/esm/cli/index.js +6 -0
- package/dist/esm/cli/utils/ast-codegen.js +93 -3
- package/dist/esm/cli/utils/ast-transformer.js +12 -0
- package/dist/esm/cli/utils/change-tracker.js +134 -0
- package/dist/esm/cli/utils/commit-manager.js +51 -0
- package/dist/esm/cli/utils/migration-generator.js +318 -0
- package/dist/esm/cli/utils/repo-manager.js +96 -3
- package/dist/esm/cli/utils/schema-diff.js +389 -0
- package/dist/esm/cli/utils/schema-hash.js +4 -0
- package/dist/esm/cli/utils/schema-to-ast.js +447 -0
- package/dist/esm/cli/utils/schema-validator.js +21 -1
- package/dist/esm/schema-definition/column-types.js +63 -10
- package/dist/esm/schema-definition/pg-enum.js +10 -0
- package/dist/esm/schema-definition/pg-function.js +19 -0
- package/dist/esm/schema-definition/pg-sequence.js +22 -1
- package/dist/esm/schema-definition/pg-trigger.js +39 -0
- package/dist/esm/schema-definition/pg-view.js +17 -0
- package/dist/esm/schema-definition/sql-expressions.js +3 -0
- package/dist/esm/schema-definition/table-definition.js +4 -0
- package/dist/index.d.ts +98 -0
- package/dist/schema-builder.d.ts +223 -24
- package/package.json +1 -1
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
export function isTableDefinition(value) {
|
|
2
|
+
return value && typeof value === 'object' &&
|
|
3
|
+
typeof value.$name === 'string' &&
|
|
4
|
+
typeof value.$columns === 'object' &&
|
|
5
|
+
typeof value.toSQL === 'function';
|
|
6
|
+
}
|
|
7
|
+
export function isEnumConfig(value) {
|
|
8
|
+
return value && typeof value === 'object' &&
|
|
9
|
+
typeof value.$enumName === 'string' &&
|
|
10
|
+
Array.isArray(value.$enumValues);
|
|
11
|
+
}
|
|
12
|
+
export function isDomainConfig(value) {
|
|
13
|
+
return value && typeof value === 'object' &&
|
|
14
|
+
typeof value.$domainName === 'string' &&
|
|
15
|
+
typeof value.$baseType === 'string';
|
|
16
|
+
}
|
|
17
|
+
export function isCompositeConfig(value) {
|
|
18
|
+
return value && typeof value === 'object' &&
|
|
19
|
+
typeof value.$typeName === 'string' &&
|
|
20
|
+
typeof value.$fields === 'object';
|
|
21
|
+
}
|
|
22
|
+
export function isSequenceConfig(value) {
|
|
23
|
+
return value && typeof value === 'object' &&
|
|
24
|
+
value._type === 'sequence' &&
|
|
25
|
+
typeof value.name === 'string';
|
|
26
|
+
}
|
|
27
|
+
export function isViewConfig(value) {
|
|
28
|
+
return value && typeof value === 'object' &&
|
|
29
|
+
value._type === 'view' &&
|
|
30
|
+
typeof value.name === 'string' &&
|
|
31
|
+
typeof value.definition === 'string';
|
|
32
|
+
}
|
|
33
|
+
export function isMaterializedViewConfig(value) {
|
|
34
|
+
return value && typeof value === 'object' &&
|
|
35
|
+
value._type === 'materialized_view' &&
|
|
36
|
+
typeof value.name === 'string' &&
|
|
37
|
+
typeof value.definition === 'string';
|
|
38
|
+
}
|
|
39
|
+
export function isFunctionConfig(value) {
|
|
40
|
+
return value && typeof value === 'object' &&
|
|
41
|
+
value.$type === 'function' &&
|
|
42
|
+
typeof value.$functionName === 'string';
|
|
43
|
+
}
|
|
44
|
+
export function isTriggerConfig(value) {
|
|
45
|
+
return value && typeof value === 'object' &&
|
|
46
|
+
value.$type === 'trigger' &&
|
|
47
|
+
typeof value.$triggerName === 'string';
|
|
48
|
+
}
|
|
49
|
+
export function isExtensionsConfig(value) {
|
|
50
|
+
return value && typeof value === 'object' &&
|
|
51
|
+
Array.isArray(value.extensions) &&
|
|
52
|
+
typeof value.toSQL === 'function';
|
|
53
|
+
}
|
|
54
|
+
export function isRelationsConfig(value) {
|
|
55
|
+
return value && typeof value === 'object' &&
|
|
56
|
+
value.$type === 'relations';
|
|
57
|
+
}
|
|
58
|
+
export function generateTrackingId() {
|
|
59
|
+
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
|
60
|
+
let result = '';
|
|
61
|
+
for (let i = 0; i < 6; i++) {
|
|
62
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
export function isValidTrackingId(id) {
|
|
67
|
+
return typeof id === 'string' && /^[a-z0-9]{6}$/.test(id);
|
|
68
|
+
}
|
|
69
|
+
export function extractTrackingId(obj) {
|
|
70
|
+
if (!obj)
|
|
71
|
+
return undefined;
|
|
72
|
+
if (typeof obj.$trackingId === 'string') {
|
|
73
|
+
return obj.$trackingId;
|
|
74
|
+
}
|
|
75
|
+
if (typeof obj.$id === 'function') {
|
|
76
|
+
const id = obj.$id();
|
|
77
|
+
if (typeof id === 'string') {
|
|
78
|
+
return id;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
export function mapColumnTypeToPostgres(type) {
|
|
84
|
+
const typeMap = {
|
|
85
|
+
'UUID': 'uuid',
|
|
86
|
+
'TEXT': 'text',
|
|
87
|
+
'VARCHAR': 'character varying',
|
|
88
|
+
'CHAR': 'character',
|
|
89
|
+
'INTEGER': 'integer',
|
|
90
|
+
'INT': 'integer',
|
|
91
|
+
'BIGINT': 'bigint',
|
|
92
|
+
'SMALLINT': 'smallint',
|
|
93
|
+
'SERIAL': 'serial',
|
|
94
|
+
'BIGSERIAL': 'bigserial',
|
|
95
|
+
'SMALLSERIAL': 'smallserial',
|
|
96
|
+
'BOOLEAN': 'boolean',
|
|
97
|
+
'BOOL': 'boolean',
|
|
98
|
+
'REAL': 'real',
|
|
99
|
+
'FLOAT4': 'real',
|
|
100
|
+
'DOUBLE PRECISION': 'double precision',
|
|
101
|
+
'FLOAT8': 'double precision',
|
|
102
|
+
'NUMERIC': 'numeric',
|
|
103
|
+
'DECIMAL': 'numeric',
|
|
104
|
+
'MONEY': 'money',
|
|
105
|
+
'DATE': 'date',
|
|
106
|
+
'TIME': 'time without time zone',
|
|
107
|
+
'TIME WITH TIME ZONE': 'time with time zone',
|
|
108
|
+
'TIMETZ': 'time with time zone',
|
|
109
|
+
'TIMESTAMP': 'timestamp without time zone',
|
|
110
|
+
'TIMESTAMP WITH TIME ZONE': 'timestamp with time zone',
|
|
111
|
+
'TIMESTAMPTZ': 'timestamp with time zone',
|
|
112
|
+
'INTERVAL': 'interval',
|
|
113
|
+
'JSON': 'json',
|
|
114
|
+
'JSONB': 'jsonb',
|
|
115
|
+
'BYTEA': 'bytea',
|
|
116
|
+
'INET': 'inet',
|
|
117
|
+
'CIDR': 'cidr',
|
|
118
|
+
'MACADDR': 'macaddr',
|
|
119
|
+
'MACADDR8': 'macaddr8',
|
|
120
|
+
'POINT': 'point',
|
|
121
|
+
'LINE': 'line',
|
|
122
|
+
'LSEG': 'lseg',
|
|
123
|
+
'BOX': 'box',
|
|
124
|
+
'PATH': 'path',
|
|
125
|
+
'POLYGON': 'polygon',
|
|
126
|
+
'CIRCLE': 'circle',
|
|
127
|
+
'TSVECTOR': 'tsvector',
|
|
128
|
+
'TSQUERY': 'tsquery',
|
|
129
|
+
'XML': 'xml',
|
|
130
|
+
'BIT': 'bit',
|
|
131
|
+
'BIT VARYING': 'bit varying',
|
|
132
|
+
'VARBIT': 'bit varying',
|
|
133
|
+
'PG_LSN': 'pg_lsn',
|
|
134
|
+
'PG_SNAPSHOT': 'pg_snapshot',
|
|
135
|
+
'INT4RANGE': 'int4range',
|
|
136
|
+
'INT8RANGE': 'int8range',
|
|
137
|
+
'NUMRANGE': 'numrange',
|
|
138
|
+
'TSRANGE': 'tsrange',
|
|
139
|
+
'TSTZRANGE': 'tstzrange',
|
|
140
|
+
'DATERANGE': 'daterange',
|
|
141
|
+
'CITEXT': 'citext',
|
|
142
|
+
'LTREE': 'ltree',
|
|
143
|
+
'HSTORE': 'hstore',
|
|
144
|
+
'CUBE': 'cube',
|
|
145
|
+
'VECTOR': 'vector',
|
|
146
|
+
};
|
|
147
|
+
const upperType = type.toUpperCase();
|
|
148
|
+
return typeMap[upperType] || type.toLowerCase();
|
|
149
|
+
}
|
|
150
|
+
export function schemaToAST(schema) {
|
|
151
|
+
const result = {
|
|
152
|
+
enums: [],
|
|
153
|
+
domains: [],
|
|
154
|
+
compositeTypes: [],
|
|
155
|
+
sequences: [],
|
|
156
|
+
tables: [],
|
|
157
|
+
views: [],
|
|
158
|
+
functions: [],
|
|
159
|
+
triggers: [],
|
|
160
|
+
extensions: [],
|
|
161
|
+
};
|
|
162
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
163
|
+
if (!value)
|
|
164
|
+
continue;
|
|
165
|
+
if (isTableDefinition(value)) {
|
|
166
|
+
const table = tableToAST(value);
|
|
167
|
+
if (table)
|
|
168
|
+
result.tables.push(table);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (isEnumConfig(value)) {
|
|
172
|
+
const enumDef = enumToAST(value);
|
|
173
|
+
if (enumDef)
|
|
174
|
+
result.enums.push(enumDef);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (isDomainConfig(value)) {
|
|
178
|
+
const domain = domainToAST(value);
|
|
179
|
+
if (domain)
|
|
180
|
+
result.domains.push(domain);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (isCompositeConfig(value)) {
|
|
184
|
+
const composite = compositeToAST(value);
|
|
185
|
+
if (composite)
|
|
186
|
+
result.compositeTypes.push(composite);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (isSequenceConfig(value)) {
|
|
190
|
+
const sequence = sequenceToAST(value);
|
|
191
|
+
if (sequence)
|
|
192
|
+
result.sequences.push(sequence);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (isViewConfig(value)) {
|
|
196
|
+
const view = viewToAST(value);
|
|
197
|
+
if (view)
|
|
198
|
+
result.views.push(view);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (isMaterializedViewConfig(value)) {
|
|
202
|
+
const matView = materializedViewToAST(value);
|
|
203
|
+
if (matView)
|
|
204
|
+
result.views.push(matView);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (isFunctionConfig(value)) {
|
|
208
|
+
const func = functionToAST(value);
|
|
209
|
+
if (func)
|
|
210
|
+
result.functions.push(func);
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (isTriggerConfig(value)) {
|
|
214
|
+
const trigger = triggerToAST(value);
|
|
215
|
+
if (trigger)
|
|
216
|
+
result.triggers.push(trigger);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (isExtensionsConfig(value)) {
|
|
220
|
+
result.extensions.push(...value.extensions);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
export function tableToAST(table) {
|
|
227
|
+
if (!isTableDefinition(table))
|
|
228
|
+
return null;
|
|
229
|
+
const columns = [];
|
|
230
|
+
for (const [colKey, colDef] of Object.entries(table.$columns || {})) {
|
|
231
|
+
const col = columnToAST(colDef, colKey);
|
|
232
|
+
if (col)
|
|
233
|
+
columns.push(col);
|
|
234
|
+
}
|
|
235
|
+
const indexes = [];
|
|
236
|
+
for (const idx of table.$indexes || []) {
|
|
237
|
+
const index = indexToAST(idx);
|
|
238
|
+
if (index)
|
|
239
|
+
indexes.push(index);
|
|
240
|
+
}
|
|
241
|
+
const constraints = [];
|
|
242
|
+
for (const con of table.$constraints || []) {
|
|
243
|
+
const constraint = constraintToAST(con);
|
|
244
|
+
if (constraint)
|
|
245
|
+
constraints.push(constraint);
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
name: table.$name,
|
|
249
|
+
schema: table.$schema || 'public',
|
|
250
|
+
columns,
|
|
251
|
+
constraints,
|
|
252
|
+
indexes,
|
|
253
|
+
isPartitioned: !!table.$partitionBy,
|
|
254
|
+
partitionType: table.$partitionBy?.type?.toUpperCase(),
|
|
255
|
+
partitionKey: table.$partitionBy?.columns,
|
|
256
|
+
comment: table.$comment,
|
|
257
|
+
trackingId: extractTrackingId(table),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
export function columnToAST(col, schemaKey) {
|
|
261
|
+
if (!col)
|
|
262
|
+
return null;
|
|
263
|
+
const config = col.$config || col;
|
|
264
|
+
return {
|
|
265
|
+
name: config.$sqlName || schemaKey,
|
|
266
|
+
type: mapColumnTypeToPostgres(config.$type || 'text'),
|
|
267
|
+
typeParams: {
|
|
268
|
+
precision: config.$precision,
|
|
269
|
+
scale: config.$scale,
|
|
270
|
+
length: config.$length,
|
|
271
|
+
},
|
|
272
|
+
isNullable: config.$nullable !== false,
|
|
273
|
+
isPrimaryKey: config.$primaryKey || false,
|
|
274
|
+
isUnique: config.$unique || false,
|
|
275
|
+
hasDefault: config.$default !== undefined,
|
|
276
|
+
defaultValue: config.$default?.toString(),
|
|
277
|
+
isGenerated: !!config.$generatedAlwaysAs,
|
|
278
|
+
generatedExpression: config.$generatedAlwaysAs?.toString(),
|
|
279
|
+
isArray: config.$isArray || false,
|
|
280
|
+
arrayDimensions: config.$arrayDimensions,
|
|
281
|
+
comment: config.$comment,
|
|
282
|
+
trackingId: extractTrackingId(config),
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
export function indexToAST(idx) {
|
|
286
|
+
if (!idx)
|
|
287
|
+
return null;
|
|
288
|
+
return {
|
|
289
|
+
name: idx.$name || idx.name,
|
|
290
|
+
columns: idx.$columns || idx.columns || [],
|
|
291
|
+
isUnique: idx.$unique || idx.unique || false,
|
|
292
|
+
method: idx.$using || idx.using || 'btree',
|
|
293
|
+
whereClause: idx.$where?.toString(),
|
|
294
|
+
includeColumns: idx.$include,
|
|
295
|
+
comment: idx.$comment,
|
|
296
|
+
trackingId: extractTrackingId(idx),
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
export function constraintToAST(con) {
|
|
300
|
+
if (!con)
|
|
301
|
+
return null;
|
|
302
|
+
return {
|
|
303
|
+
name: con.$name || con.name,
|
|
304
|
+
type: (con.$type || con.type || 'CHECK').toUpperCase(),
|
|
305
|
+
columns: con.$columns || con.columns || [],
|
|
306
|
+
expression: con.$expression || con.expression,
|
|
307
|
+
trackingId: extractTrackingId(con),
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
export function enumToAST(enumDef) {
|
|
311
|
+
if (!isEnumConfig(enumDef))
|
|
312
|
+
return null;
|
|
313
|
+
return {
|
|
314
|
+
name: enumDef.$enumName,
|
|
315
|
+
schema: enumDef.$schema || 'public',
|
|
316
|
+
values: [...enumDef.$enumValues],
|
|
317
|
+
trackingId: extractTrackingId(enumDef),
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
export function domainToAST(domain) {
|
|
321
|
+
if (!isDomainConfig(domain))
|
|
322
|
+
return null;
|
|
323
|
+
let checkExpression;
|
|
324
|
+
let checkName;
|
|
325
|
+
if (domain.$constraints && domain.$constraints.length > 0) {
|
|
326
|
+
const constraint = domain.$constraints[0];
|
|
327
|
+
const namedMatch = constraint.match(/^CONSTRAINT\s+(\w+)\s+CHECK\s*\((.+)\)$/i);
|
|
328
|
+
if (namedMatch) {
|
|
329
|
+
checkName = namedMatch[1];
|
|
330
|
+
checkExpression = namedMatch[2];
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
const simpleMatch = constraint.match(/^CHECK\s*\((.+)\)$/i);
|
|
334
|
+
if (simpleMatch) {
|
|
335
|
+
checkExpression = simpleMatch[1];
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
name: domain.$domainName,
|
|
341
|
+
schema: domain.$schema || 'public',
|
|
342
|
+
baseType: domain.$baseType,
|
|
343
|
+
notNull: domain.$notNull || false,
|
|
344
|
+
defaultValue: domain.$domainDefault?.toString(),
|
|
345
|
+
checkExpression,
|
|
346
|
+
checkName,
|
|
347
|
+
trackingId: extractTrackingId(domain),
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
export function compositeToAST(composite) {
|
|
351
|
+
if (!isCompositeConfig(composite))
|
|
352
|
+
return null;
|
|
353
|
+
const attributes = [];
|
|
354
|
+
for (const [fieldName, fieldDef] of Object.entries(composite.$fields || {})) {
|
|
355
|
+
const config = fieldDef.$config || fieldDef;
|
|
356
|
+
attributes.push({
|
|
357
|
+
name: fieldName,
|
|
358
|
+
type: config.$type || 'text',
|
|
359
|
+
collation: config.$collation,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
return {
|
|
363
|
+
name: composite.$typeName,
|
|
364
|
+
schema: composite.$schema || 'public',
|
|
365
|
+
attributes,
|
|
366
|
+
trackingId: extractTrackingId(composite),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
export function sequenceToAST(seq) {
|
|
370
|
+
if (!isSequenceConfig(seq))
|
|
371
|
+
return null;
|
|
372
|
+
return {
|
|
373
|
+
name: seq.name,
|
|
374
|
+
schema: seq.schema || 'public',
|
|
375
|
+
startValue: seq.options?.start,
|
|
376
|
+
increment: seq.options?.increment,
|
|
377
|
+
minValue: seq.options?.minValue,
|
|
378
|
+
maxValue: seq.options?.maxValue,
|
|
379
|
+
cache: seq.options?.cache,
|
|
380
|
+
cycle: seq.options?.cycle || false,
|
|
381
|
+
ownedBy: seq.options?.ownedBy,
|
|
382
|
+
trackingId: extractTrackingId(seq),
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
export function viewToAST(view) {
|
|
386
|
+
if (!isViewConfig(view))
|
|
387
|
+
return null;
|
|
388
|
+
return {
|
|
389
|
+
name: view.name,
|
|
390
|
+
schema: view.schema || 'public',
|
|
391
|
+
definition: view.definition,
|
|
392
|
+
isMaterialized: false,
|
|
393
|
+
trackingId: extractTrackingId(view),
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
export function materializedViewToAST(matView) {
|
|
397
|
+
if (!isMaterializedViewConfig(matView))
|
|
398
|
+
return null;
|
|
399
|
+
return {
|
|
400
|
+
name: matView.name,
|
|
401
|
+
schema: matView.schema || 'public',
|
|
402
|
+
definition: matView.definition,
|
|
403
|
+
isMaterialized: true,
|
|
404
|
+
withData: matView.withData,
|
|
405
|
+
trackingId: extractTrackingId(matView),
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
export function functionToAST(func) {
|
|
409
|
+
if (!isFunctionConfig(func))
|
|
410
|
+
return null;
|
|
411
|
+
const opts = func.$options || {};
|
|
412
|
+
return {
|
|
413
|
+
name: func.$functionName,
|
|
414
|
+
schema: opts.schema || 'public',
|
|
415
|
+
args: (opts.args || []).map((arg) => ({
|
|
416
|
+
name: arg.name,
|
|
417
|
+
type: arg.type,
|
|
418
|
+
mode: arg.mode,
|
|
419
|
+
default: arg.default,
|
|
420
|
+
})),
|
|
421
|
+
returnType: opts.returns || 'void',
|
|
422
|
+
language: opts.language || 'plpgsql',
|
|
423
|
+
body: opts.body || '',
|
|
424
|
+
volatility: opts.volatility?.toUpperCase(),
|
|
425
|
+
isStrict: opts.strict || false,
|
|
426
|
+
securityDefiner: opts.securityDefiner || false,
|
|
427
|
+
trackingId: extractTrackingId(func),
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
export function triggerToAST(trigger) {
|
|
431
|
+
if (!isTriggerConfig(trigger))
|
|
432
|
+
return null;
|
|
433
|
+
const opts = trigger.$options || {};
|
|
434
|
+
return {
|
|
435
|
+
name: trigger.$triggerName,
|
|
436
|
+
table: opts.table || '',
|
|
437
|
+
timing: (opts.timing || 'BEFORE').toUpperCase(),
|
|
438
|
+
events: (opts.events || ['INSERT']).map((e) => e.toUpperCase()),
|
|
439
|
+
forEach: (opts.forEach || 'ROW').toUpperCase(),
|
|
440
|
+
functionName: opts.execute || '',
|
|
441
|
+
whenClause: opts.when,
|
|
442
|
+
isConstraint: opts.constraint || false,
|
|
443
|
+
deferrable: opts.deferrable,
|
|
444
|
+
initiallyDeferred: opts.initiallyDeferred,
|
|
445
|
+
trackingId: extractTrackingId(trigger),
|
|
446
|
+
};
|
|
447
|
+
}
|
|
@@ -52,7 +52,27 @@ export function validateSchemaFile(schemaPath) {
|
|
|
52
52
|
getNewLine: () => '\n',
|
|
53
53
|
});
|
|
54
54
|
const syntacticDiagnostics = program.getSyntacticDiagnostics(sourceFile);
|
|
55
|
-
|
|
55
|
+
const ignoredErrorCodes = new Set([
|
|
56
|
+
2306,
|
|
57
|
+
2307,
|
|
58
|
+
2305,
|
|
59
|
+
2339,
|
|
60
|
+
2694,
|
|
61
|
+
2792,
|
|
62
|
+
2503,
|
|
63
|
+
2749,
|
|
64
|
+
2315,
|
|
65
|
+
2322,
|
|
66
|
+
2345,
|
|
67
|
+
2769,
|
|
68
|
+
2741,
|
|
69
|
+
2551,
|
|
70
|
+
2737,
|
|
71
|
+
]);
|
|
72
|
+
const semanticDiagnostics = program.getSemanticDiagnostics(sourceFile)
|
|
73
|
+
.filter(d => !ignoredErrorCodes.has(d.code));
|
|
74
|
+
const allDiagnostics = [...syntacticDiagnostics, ...semanticDiagnostics];
|
|
75
|
+
for (const diagnostic of allDiagnostics) {
|
|
56
76
|
if (diagnostic.file && diagnostic.start !== undefined) {
|
|
57
77
|
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
|
58
78
|
errors.push({
|
|
@@ -228,6 +228,7 @@ const generatedFnMethods = {
|
|
|
228
228
|
cast: (value, typeName) => chainableExpr(`(${formatGenValue(value)})::${typeName}`),
|
|
229
229
|
func: (name, ...args) => chainableExpr(`${name.toUpperCase()}(${args.map(formatGenValue).join(', ')})`),
|
|
230
230
|
sql: (expression) => chainableExpr(expression),
|
|
231
|
+
raw: (expression) => chainableExpr(expression),
|
|
231
232
|
op: (left, operator, right) => chainableExpr(`(${formatGenValue(left)} ${operator} ${formatGenValue(right)})`),
|
|
232
233
|
setweight: (tsvector, weight) => chainableExpr(`SETWEIGHT(${tsvector.$sql}, '${weight}')`),
|
|
233
234
|
setWeight: (tsvector, weight) => chainableExpr(`SETWEIGHT(${tsvector.$sql}, '${weight}')`),
|
|
@@ -300,12 +301,18 @@ function createColumn(type) {
|
|
|
300
301
|
return Object.assign(this, { $generated: config.$generated });
|
|
301
302
|
},
|
|
302
303
|
generatedAlwaysAs(callback, options) {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
304
|
+
let result;
|
|
305
|
+
if (callback.length === 1) {
|
|
306
|
+
result = callback(generatedFn);
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
const tableProxy = new Proxy({}, {
|
|
310
|
+
get(_target, prop) {
|
|
311
|
+
return chainableExpr(`"${prop}"`);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
result = callback(tableProxy, generatedFn);
|
|
315
|
+
}
|
|
309
316
|
const stored = options?.stored !== false;
|
|
310
317
|
config.$generated = { expression: result.$sql, stored };
|
|
311
318
|
return Object.assign(this, { $generated: config.$generated });
|
|
@@ -777,7 +784,21 @@ export function pgDomain(name, baseType, checks) {
|
|
|
777
784
|
}
|
|
778
785
|
return col;
|
|
779
786
|
};
|
|
780
|
-
|
|
787
|
+
let checkExpression;
|
|
788
|
+
let checkName;
|
|
789
|
+
if (constraints.length > 0) {
|
|
790
|
+
const firstConstraint = constraints[0];
|
|
791
|
+
const namedMatch = firstConstraint.match(/^CONSTRAINT\s+(\w+)\s+CHECK\s*\((.+)\)$/i);
|
|
792
|
+
const unnamedMatch = firstConstraint.match(/^CHECK\s*\((.+)\)$/i);
|
|
793
|
+
if (namedMatch) {
|
|
794
|
+
checkName = namedMatch[1];
|
|
795
|
+
checkExpression = namedMatch[2];
|
|
796
|
+
}
|
|
797
|
+
else if (unnamedMatch) {
|
|
798
|
+
checkExpression = unnamedMatch[1];
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
const domainConfig = {
|
|
781
802
|
$domainName: name,
|
|
782
803
|
$baseType: baseTypeStr,
|
|
783
804
|
$constraints: constraints.length > 0 ? constraints : undefined,
|
|
@@ -785,7 +806,22 @@ export function pgDomain(name, baseType, checks) {
|
|
|
785
806
|
$notNull: notNull,
|
|
786
807
|
$columnBuilder: baseType,
|
|
787
808
|
$validate: validateFn,
|
|
788
|
-
|
|
809
|
+
$trackingId: undefined,
|
|
810
|
+
toAST() {
|
|
811
|
+
return {
|
|
812
|
+
name: domainConfig.$domainName,
|
|
813
|
+
baseType: domainConfig.$baseType,
|
|
814
|
+
notNull: domainConfig.$notNull ?? false,
|
|
815
|
+
defaultValue: domainConfig.$domainDefault !== undefined
|
|
816
|
+
? String(domainConfig.$domainDefault)
|
|
817
|
+
: undefined,
|
|
818
|
+
checkExpression,
|
|
819
|
+
checkName,
|
|
820
|
+
trackingId: domainConfig.$trackingId,
|
|
821
|
+
};
|
|
822
|
+
},
|
|
823
|
+
};
|
|
824
|
+
Object.assign(domainFn, domainConfig);
|
|
789
825
|
return domainFn;
|
|
790
826
|
}
|
|
791
827
|
export function generateDomainSQL(domain) {
|
|
@@ -807,11 +843,28 @@ export function pgComposite(name, fields) {
|
|
|
807
843
|
col.$fields = fields;
|
|
808
844
|
return col;
|
|
809
845
|
};
|
|
810
|
-
Object.
|
|
846
|
+
const attributes = Object.entries(fields).map(([fieldName, fieldConfig]) => {
|
|
847
|
+
const config = fieldConfig;
|
|
848
|
+
return {
|
|
849
|
+
name: fieldName,
|
|
850
|
+
type: config.$type,
|
|
851
|
+
collation: undefined,
|
|
852
|
+
};
|
|
853
|
+
});
|
|
854
|
+
const compositeConfig = {
|
|
811
855
|
$typeName: name,
|
|
812
856
|
$fields: fields,
|
|
813
857
|
$inferType: {},
|
|
814
|
-
|
|
858
|
+
$trackingId: undefined,
|
|
859
|
+
toAST() {
|
|
860
|
+
return {
|
|
861
|
+
name: compositeConfig.$typeName,
|
|
862
|
+
attributes,
|
|
863
|
+
trackingId: compositeConfig.$trackingId,
|
|
864
|
+
};
|
|
865
|
+
},
|
|
866
|
+
};
|
|
867
|
+
Object.assign(compositeFn, compositeConfig);
|
|
815
868
|
return compositeFn;
|
|
816
869
|
}
|
|
817
870
|
export function generateCompositeTypeSQL(composite) {
|
|
@@ -48,6 +48,16 @@ export function pgEnum(name, values) {
|
|
|
48
48
|
},
|
|
49
49
|
enumerable: true,
|
|
50
50
|
},
|
|
51
|
+
toAST: {
|
|
52
|
+
value: function () {
|
|
53
|
+
return {
|
|
54
|
+
name: this.$enumName,
|
|
55
|
+
values: [...this.$enumValues],
|
|
56
|
+
trackingId: this.$trackingId,
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
enumerable: true,
|
|
60
|
+
},
|
|
51
61
|
});
|
|
52
62
|
return config;
|
|
53
63
|
}
|
|
@@ -75,6 +75,25 @@ export function pgFunction(name, options) {
|
|
|
75
75
|
$functionName: name,
|
|
76
76
|
$options: options,
|
|
77
77
|
$type: 'function',
|
|
78
|
+
toAST() {
|
|
79
|
+
const opts = this.$options;
|
|
80
|
+
return {
|
|
81
|
+
name: this.$functionName,
|
|
82
|
+
args: (opts.args || []).map(arg => ({
|
|
83
|
+
name: arg.name,
|
|
84
|
+
type: arg.type,
|
|
85
|
+
mode: arg.mode,
|
|
86
|
+
default: arg.default,
|
|
87
|
+
})),
|
|
88
|
+
returnType: opts.returns,
|
|
89
|
+
language: opts.language || 'plpgsql',
|
|
90
|
+
body: opts.body || opts.raw || '',
|
|
91
|
+
volatility: opts.volatility,
|
|
92
|
+
isStrict: opts.strict || false,
|
|
93
|
+
securityDefiner: opts.security === 'DEFINER',
|
|
94
|
+
trackingId: this.$trackingId,
|
|
95
|
+
};
|
|
96
|
+
},
|
|
78
97
|
};
|
|
79
98
|
}
|
|
80
99
|
export function isFunctionConfig(value) {
|
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
export function pgSequence(name, options = {}) {
|
|
2
|
-
|
|
2
|
+
const config = {
|
|
3
3
|
_type: 'sequence',
|
|
4
4
|
name,
|
|
5
5
|
options,
|
|
6
|
+
toAST() {
|
|
7
|
+
return {
|
|
8
|
+
name: this.name,
|
|
9
|
+
startValue: this.options.start,
|
|
10
|
+
increment: this.options.increment,
|
|
11
|
+
minValue: this.options.minValue ?? undefined,
|
|
12
|
+
maxValue: this.options.maxValue ?? undefined,
|
|
13
|
+
cache: this.options.cache,
|
|
14
|
+
cycle: this.options.cycle ?? false,
|
|
15
|
+
ownedBy: this.options.ownedBy ? parseOwnedBy(this.options.ownedBy) : undefined,
|
|
16
|
+
trackingId: this.$trackingId,
|
|
17
|
+
};
|
|
18
|
+
},
|
|
6
19
|
};
|
|
20
|
+
return config;
|
|
21
|
+
}
|
|
22
|
+
function parseOwnedBy(ownedBy) {
|
|
23
|
+
const parts = ownedBy.split('.');
|
|
24
|
+
if (parts.length === 2) {
|
|
25
|
+
return { table: parts[0], column: parts[1] };
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
7
28
|
}
|
|
8
29
|
export function generateSequenceSQL(seq) {
|
|
9
30
|
const parts = [`CREATE SEQUENCE ${seq.name}`];
|