@anthonyalayo/drizzle-kit 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/api.d.mts +2891 -0
  2. package/api.d.ts +2891 -0
  3. package/api.js +47534 -0
  4. package/api.mjs +47521 -0
  5. package/bin.cjs +80043 -0
  6. package/{src/index.ts → index-Z-1TKnbX.d.mts} +123 -141
  7. package/index-Z-1TKnbX.d.ts +308 -0
  8. package/index.d.mts +2 -0
  9. package/index.d.ts +2 -0
  10. package/index.js +32 -0
  11. package/index.mjs +7 -0
  12. package/package.json +1 -1
  13. package/utils.js +6056 -0
  14. package/utils.mjs +6032 -0
  15. package/.gitignore +0 -25
  16. package/build.dev.ts +0 -51
  17. package/build.ts +0 -112
  18. package/imports-checker/analyze.ts +0 -78
  19. package/imports-checker/checker.ts +0 -296
  20. package/imports-checker/grammar/grammar.ohm +0 -121
  21. package/imports-checker/grammar/grammar.ohm-bundle.d.ts +0 -164
  22. package/imports-checker/grammar/grammar.ohm-bundle.js +0 -753
  23. package/imports-checker/index.ts +0 -48
  24. package/patches/difflib@0.2.4.patch +0 -31
  25. package/src/@types/utils.ts +0 -58
  26. package/src/api.ts +0 -504
  27. package/src/cli/commands/_es5.ts +0 -2
  28. package/src/cli/commands/check.ts +0 -52
  29. package/src/cli/commands/drop.ts +0 -60
  30. package/src/cli/commands/introspect.ts +0 -772
  31. package/src/cli/commands/libSqlPushUtils.ts +0 -355
  32. package/src/cli/commands/migrate.ts +0 -1526
  33. package/src/cli/commands/mysqlIntrospect.ts +0 -53
  34. package/src/cli/commands/mysqlPushUtils.ts +0 -352
  35. package/src/cli/commands/mysqlUp.ts +0 -102
  36. package/src/cli/commands/pgIntrospect.ts +0 -55
  37. package/src/cli/commands/pgPushUtils.ts +0 -269
  38. package/src/cli/commands/pgUp.ts +0 -217
  39. package/src/cli/commands/push.ts +0 -637
  40. package/src/cli/commands/singlestoreIntrospect.ts +0 -53
  41. package/src/cli/commands/singlestorePushUtils.ts +0 -456
  42. package/src/cli/commands/singlestoreUp.ts +0 -1
  43. package/src/cli/commands/sqliteIntrospect.ts +0 -96
  44. package/src/cli/commands/sqlitePushUtils.ts +0 -322
  45. package/src/cli/commands/sqliteUp.ts +0 -52
  46. package/src/cli/commands/utils.ts +0 -858
  47. package/src/cli/connections.ts +0 -914
  48. package/src/cli/index.ts +0 -48
  49. package/src/cli/schema.ts +0 -794
  50. package/src/cli/selector-ui.ts +0 -38
  51. package/src/cli/utils.ts +0 -112
  52. package/src/cli/validations/cli.ts +0 -79
  53. package/src/cli/validations/common.ts +0 -193
  54. package/src/cli/validations/libsql.ts +0 -27
  55. package/src/cli/validations/mysql.ts +0 -61
  56. package/src/cli/validations/outputs.ts +0 -91
  57. package/src/cli/validations/postgres.ts +0 -83
  58. package/src/cli/validations/singlestore.ts +0 -61
  59. package/src/cli/validations/sqlite.ts +0 -104
  60. package/src/cli/validations/studio.ts +0 -24
  61. package/src/cli/views.ts +0 -658
  62. package/src/extensions/getTablesFilterByExtensions.ts +0 -16
  63. package/src/extensions/vector.ts +0 -10
  64. package/src/global.ts +0 -61
  65. package/src/introspect-mysql.ts +0 -1030
  66. package/src/introspect-pg.ts +0 -1435
  67. package/src/introspect-singlestore.ts +0 -913
  68. package/src/introspect-sqlite.ts +0 -533
  69. package/src/jsonDiffer.js +0 -882
  70. package/src/jsonStatements.ts +0 -3590
  71. package/src/loader.mjs +0 -57
  72. package/src/migrationPreparator.ts +0 -217
  73. package/src/schemaValidator.ts +0 -23
  74. package/src/serializer/index.ts +0 -142
  75. package/src/serializer/mysqlImports.ts +0 -38
  76. package/src/serializer/mysqlSchema.ts +0 -423
  77. package/src/serializer/mysqlSerializer.ts +0 -999
  78. package/src/serializer/pgImports.ts +0 -106
  79. package/src/serializer/pgSchema.ts +0 -937
  80. package/src/serializer/pgSerializer.ts +0 -2223
  81. package/src/serializer/singlestoreImports.ts +0 -38
  82. package/src/serializer/singlestoreSchema.ts +0 -257
  83. package/src/serializer/singlestoreSerializer.ts +0 -767
  84. package/src/serializer/sqliteImports.ts +0 -41
  85. package/src/serializer/sqliteSchema.ts +0 -352
  86. package/src/serializer/sqliteSerializer.ts +0 -931
  87. package/src/serializer/studio.ts +0 -694
  88. package/src/serializer/utils.ts +0 -45
  89. package/src/simulator.ts +0 -157
  90. package/src/snapshotsDiffer.ts +0 -4398
  91. package/src/sqlgenerator.ts +0 -4212
  92. package/src/statementCombiner.ts +0 -597
  93. package/src/utils/certs.ts +0 -44
  94. package/src/utils/words.ts +0 -1333
  95. package/src/utils.ts +0 -370
  96. package/tests/bin.test.ts +0 -58
  97. package/tests/cli/d1http.config.ts +0 -12
  98. package/tests/cli/drizzle.config.ts +0 -9
  99. package/tests/cli/durable-sqlite.config.ts +0 -7
  100. package/tests/cli/expo.config.ts +0 -7
  101. package/tests/cli/postgres.config.ts +0 -13
  102. package/tests/cli/postgres2.config.ts +0 -17
  103. package/tests/cli/schema.ts +0 -1
  104. package/tests/cli/turso.config.ts +0 -10
  105. package/tests/cli-export.test.ts +0 -79
  106. package/tests/cli-generate.test.ts +0 -259
  107. package/tests/cli-migrate.test.ts +0 -103
  108. package/tests/cli-push.test.ts +0 -126
  109. package/tests/common.ts +0 -15
  110. package/tests/indexes/common.ts +0 -21
  111. package/tests/indexes/pg.test.ts +0 -245
  112. package/tests/introspect/libsql.test.ts +0 -35
  113. package/tests/introspect/mysql.test.ts +0 -319
  114. package/tests/introspect/pg.test.ts +0 -894
  115. package/tests/introspect/singlestore.test.ts +0 -275
  116. package/tests/introspect/sqlite.test.ts +0 -122
  117. package/tests/libsql-checks.test.ts +0 -308
  118. package/tests/libsql-statements.test.ts +0 -989
  119. package/tests/libsql-views.test.ts +0 -218
  120. package/tests/migrate/libsq-schema.ts +0 -6
  121. package/tests/migrate/libsql-migrate.test.ts +0 -58
  122. package/tests/migrate/migrations/0000_little_blizzard.sql +0 -4
  123. package/tests/migrate/migrations/0001_nebulous_storm.sql +0 -10
  124. package/tests/migrate/migrations/meta/0000_snapshot.json +0 -40
  125. package/tests/migrate/migrations/meta/0001_snapshot.json +0 -40
  126. package/tests/migrate/migrations/meta/_journal.json +0 -20
  127. package/tests/mysql-checks.test.ts +0 -291
  128. package/tests/mysql-generated.test.ts +0 -1290
  129. package/tests/mysql-schemas.test.ts +0 -156
  130. package/tests/mysql-views.test.ts +0 -553
  131. package/tests/mysql.test.ts +0 -863
  132. package/tests/pg-array.test.ts +0 -368
  133. package/tests/pg-checks.test.ts +0 -329
  134. package/tests/pg-columns.test.ts +0 -484
  135. package/tests/pg-domains.test.ts +0 -306
  136. package/tests/pg-enums.test.ts +0 -751
  137. package/tests/pg-generated.test.ts +0 -529
  138. package/tests/pg-identity.test.ts +0 -447
  139. package/tests/pg-schemas.test.ts +0 -105
  140. package/tests/pg-sequences.test.ts +0 -298
  141. package/tests/pg-tables.test.ts +0 -1021
  142. package/tests/pg-views.test.ts +0 -1929
  143. package/tests/push/common.ts +0 -58
  144. package/tests/push/libsql.test.ts +0 -1400
  145. package/tests/push/mysql-push.test.ts +0 -345
  146. package/tests/push/mysql.test.ts +0 -788
  147. package/tests/push/pg.test.ts +0 -4341
  148. package/tests/push/singlestore-push.test.ts +0 -894
  149. package/tests/push/singlestore.test.ts +0 -444
  150. package/tests/push/sqlite.test.ts +0 -1613
  151. package/tests/rls/pg-policy.test.ts +0 -1656
  152. package/tests/rls/pg-role.test.ts +0 -234
  153. package/tests/schemaDiffer.ts +0 -2852
  154. package/tests/singlestore-generated.test.ts +0 -1290
  155. package/tests/singlestore-schemas.test.ts +0 -155
  156. package/tests/singlestore.test.ts +0 -978
  157. package/tests/sqlite-checks.test.ts +0 -308
  158. package/tests/sqlite-columns.test.ts +0 -1049
  159. package/tests/sqlite-generated.test.ts +0 -1726
  160. package/tests/sqlite-tables.test.ts +0 -637
  161. package/tests/sqlite-views.test.ts +0 -218
  162. package/tests/statements-combiner/libsql-statements-combiner.test.ts +0 -1812
  163. package/tests/statements-combiner/singlestore-statements-combiner.test.ts +0 -882
  164. package/tests/statements-combiner/sqlite-statements-combiner.test.ts +0 -1211
  165. package/tests/test/sqlite.test.ts +0 -39
  166. package/tests/testsinglestore.ts +0 -29
  167. package/tests/validations.test.ts +0 -869
  168. package/tests/wrap-param.test.ts +0 -16
  169. package/tsconfig.build.json +0 -4
  170. package/tsconfig.cli-types.json +0 -9
  171. package/tsconfig.json +0 -28
  172. package/vitest.config.ts +0 -34
@@ -1,2223 +0,0 @@
1
- import chalk from 'chalk';
2
- import { ColumnBuilderBaseConfig, getTableName, is, SQL } from 'drizzle-orm';
3
- import {
4
- AnyPgTable,
5
- getMaterializedViewConfig,
6
- getTableConfig,
7
- getViewConfig,
8
- IndexedColumn,
9
- PgColumn,
10
- PgColumnBuilder,
11
- PgDialect,
12
- PgDomain,
13
- PgDomainColumn,
14
- PgEnum,
15
- PgEnumColumn,
16
- PgMaterializedView,
17
- PgPolicy,
18
- PgRole,
19
- PgSchema,
20
- PgSequence,
21
- PgView,
22
- uniqueKeyName,
23
- } from 'drizzle-orm/pg-core';
24
- import { CasingType } from 'src/cli/validations/common';
25
- import { vectorOps } from 'src/extensions/vector';
26
- import { withStyle } from '../cli/validations/outputs';
27
- import type { IntrospectStage, IntrospectStatus } from '../cli/views';
28
- import { snapshotVersion } from '../global';
29
- import { type DB, escapeSingleQuotes, isPgArrayType } from '../utils';
30
- import {
31
- CheckConstraint,
32
- Column,
33
- Domain,
34
- Enum,
35
- ForeignKey,
36
- Index,
37
- IndexColumnType,
38
- PgKitInternals,
39
- PgSchemaInternal,
40
- PgSquasher,
41
- Policy,
42
- PrimaryKey,
43
- Role,
44
- Sequence,
45
- Table,
46
- UniqueConstraint,
47
- View,
48
- } from './pgSchema';
49
- import { getColumnCasing, sqlToStr } from './utils';
50
-
51
- export const indexName = (tableName: string, columns: string[]) => {
52
- return `${tableName}_${columns.join('_')}_index`;
53
- };
54
-
55
- function stringFromIdentityProperty(field: string | number | undefined): string | undefined {
56
- return typeof field === 'string' ? (field as string) : typeof field === 'undefined' ? undefined : String(field);
57
- }
58
-
59
- function maxRangeForIdentityBasedOn(columnType: string) {
60
- return columnType === 'integer' ? '2147483647' : columnType === 'bigint' ? '9223372036854775807' : '32767';
61
- }
62
-
63
- function minRangeForIdentityBasedOn(columnType: string) {
64
- return columnType === 'integer' ? '-2147483648' : columnType === 'bigint' ? '-9223372036854775808' : '-32768';
65
- }
66
-
67
- function stringFromDatabaseIdentityProperty(field: any): string | undefined {
68
- return typeof field === 'string'
69
- ? (field as string)
70
- : typeof field === 'undefined'
71
- ? undefined
72
- : typeof field === 'bigint'
73
- ? field.toString()
74
- : String(field);
75
- }
76
-
77
- export function buildArrayString(array: any[], sqlType: string): string {
78
- sqlType = sqlType.split('[')[0];
79
- const values = array
80
- .map((value) => {
81
- if (typeof value === 'number' || typeof value === 'bigint') {
82
- return value.toString();
83
- } else if (typeof value === 'boolean') {
84
- return value ? 'true' : 'false';
85
- } else if (Array.isArray(value)) {
86
- return buildArrayString(value, sqlType);
87
- } else if (value instanceof Date) {
88
- if (sqlType === 'date') {
89
- return `"${value.toISOString().split('T')[0]}"`;
90
- } else if (sqlType === 'timestamp') {
91
- return `"${value.toISOString().replace('T', ' ').slice(0, 23)}"`;
92
- } else {
93
- return `"${value.toISOString()}"`;
94
- }
95
- } else if (typeof value === 'object') {
96
- return `"${JSON.stringify(value).replaceAll('"', '\\"')}"`;
97
- }
98
-
99
- return `"${value}"`;
100
- })
101
- .join(',');
102
-
103
- return `{${values}}`;
104
- }
105
-
106
- export const generatePgSnapshot = (
107
- tables: AnyPgTable[],
108
- domains: PgDomain<any>[],
109
- enums: PgEnum<any>[],
110
- schemas: PgSchema[],
111
- sequences: PgSequence[],
112
- roles: PgRole[],
113
- policies: PgPolicy[],
114
- views: PgView[],
115
- matViews: PgMaterializedView[],
116
- casing: CasingType | undefined,
117
- schemaFilter?: string[],
118
- ): PgSchemaInternal => {
119
- const dialect = new PgDialect({ casing });
120
- const result: Record<string, Table> = {};
121
- const resultViews: Record<string, View> = {};
122
- const sequencesToReturn: Record<string, Sequence> = {};
123
- const rolesToReturn: Record<string, Role> = {};
124
- // this policies are a separate objects that were linked to a table outside of it
125
- const policiesToReturn: Record<string, Policy> = {};
126
-
127
- // This object stores unique names for indexes and will be used to detect if you have the same names for indexes
128
- // within the same PostgreSQL schema
129
-
130
- const indexesInSchema: Record<string, string[]> = {};
131
-
132
- // This object stores unique names for checks and will be used to detect if you have the same names for checks
133
- // within the same PostgreSQL table
134
- const checksInTable: Record<string, string[]> = {};
135
-
136
- for (const table of tables) {
137
- const {
138
- name: tableName,
139
- columns,
140
- indexes,
141
- foreignKeys,
142
- checks,
143
- schema,
144
- primaryKeys,
145
- uniqueConstraints,
146
- policies,
147
- enableRLS,
148
- } = getTableConfig(table);
149
-
150
- if (schemaFilter && !schemaFilter.includes(schema ?? 'public')) {
151
- continue;
152
- }
153
-
154
- const columnsObject: Record<string, Column> = {};
155
- const indexesObject: Record<string, Index> = {};
156
- const checksObject: Record<string, CheckConstraint> = {};
157
- const foreignKeysObject: Record<string, ForeignKey> = {};
158
- const primaryKeysObject: Record<string, PrimaryKey> = {};
159
- const uniqueConstraintObject: Record<string, UniqueConstraint> = {};
160
- const policiesObject: Record<string, Policy> = {};
161
-
162
- columns.forEach((column) => {
163
- const name = getColumnCasing(column, casing);
164
- const notNull: boolean = column.notNull;
165
- const primaryKey: boolean = column.primary;
166
- const sqlTypeLowered = column.getSQLType().toLowerCase();
167
-
168
- // todo understand this
169
- const typeSchema = is(column, PgEnumColumn) ? column.enum.schema || 'public' : undefined;
170
- const domainTypeSchema = is(column, PgDomainColumn) ? column.schema : undefined;
171
- const generated = column.generated;
172
- const identity = column.generatedIdentity;
173
- const increment = stringFromIdentityProperty(identity?.sequenceOptions?.increment) ?? '1';
174
- const minValue = stringFromIdentityProperty(identity?.sequenceOptions?.minValue)
175
- ?? (parseFloat(increment) < 0 ? minRangeForIdentityBasedOn(column.columnType) : '1');
176
- const maxValue = stringFromIdentityProperty(identity?.sequenceOptions?.maxValue)
177
- ?? (parseFloat(increment) < 0 ? '-1' : maxRangeForIdentityBasedOn(column.getSQLType()));
178
- const startWith = stringFromIdentityProperty(identity?.sequenceOptions?.startWith)
179
- ?? (parseFloat(increment) < 0 ? maxValue : minValue);
180
- const cache = stringFromIdentityProperty(identity?.sequenceOptions?.cache) ?? '1';
181
-
182
- const columnToSet: Column = {
183
- name,
184
- type: column.getSQLType(),
185
- typeSchema,
186
- domainTypeSchema,
187
- primaryKey,
188
- notNull,
189
- generated: generated
190
- ? {
191
- as: is(generated.as, SQL)
192
- ? dialect.sqlToQuery(generated.as as SQL).sql
193
- : typeof generated.as === 'function'
194
- ? dialect.sqlToQuery(generated.as() as SQL).sql
195
- : (generated.as as any),
196
- type: 'stored',
197
- }
198
- : undefined,
199
- identity: identity
200
- ? {
201
- type: identity.type,
202
- name: identity.sequenceName ?? `${tableName}_${name}_seq`,
203
- schema: schema ?? 'public',
204
- increment,
205
- startWith,
206
- minValue,
207
- maxValue,
208
- cache,
209
- cycle: identity?.sequenceOptions?.cycle ?? false,
210
- }
211
- : undefined,
212
- };
213
-
214
- if (column.isUnique) {
215
- const existingUnique = uniqueConstraintObject[column.uniqueName!];
216
- if (typeof existingUnique !== 'undefined') {
217
- console.log(
218
- `\n${
219
- withStyle.errorWarning(`We\'ve found duplicated unique constraint names in ${
220
- chalk.underline.blue(
221
- tableName,
222
- )
223
- } table.
224
- The unique constraint ${
225
- chalk.underline.blue(
226
- column.uniqueName,
227
- )
228
- } on the ${
229
- chalk.underline.blue(
230
- name,
231
- )
232
- } column is conflicting with a unique constraint name already defined for ${
233
- chalk.underline.blue(
234
- existingUnique.columns.join(','),
235
- )
236
- } columns\n`)
237
- }`,
238
- );
239
- process.exit(1);
240
- }
241
- uniqueConstraintObject[column.uniqueName!] = {
242
- name: column.uniqueName!,
243
- nullsNotDistinct: column.uniqueType === 'not distinct',
244
- columns: [columnToSet.name],
245
- };
246
- }
247
-
248
- if (column.default !== undefined) {
249
- if (is(column.default, SQL)) {
250
- columnToSet.default = sqlToStr(column.default, casing);
251
- } else {
252
- if (typeof column.default === 'string') {
253
- columnToSet.default = `'${escapeSingleQuotes(column.default)}'`;
254
- } else {
255
- if (sqlTypeLowered === 'jsonb' || sqlTypeLowered === 'json') {
256
- columnToSet.default = `'${JSON.stringify(column.default)}'::${sqlTypeLowered}`;
257
- } else if (column.default instanceof Date) {
258
- if (sqlTypeLowered === 'date') {
259
- columnToSet.default = `'${column.default.toISOString().split('T')[0]}'`;
260
- } else if (sqlTypeLowered === 'timestamp') {
261
- columnToSet.default = `'${column.default.toISOString().replace('T', ' ').slice(0, 23)}'`;
262
- } else {
263
- columnToSet.default = `'${column.default.toISOString()}'`;
264
- }
265
- } else if (isPgArrayType(sqlTypeLowered) && Array.isArray(column.default)) {
266
- columnToSet.default = `'${buildArrayString(column.default, sqlTypeLowered)}'`;
267
- } else {
268
- // Should do for all types
269
- // columnToSet.default = `'${column.default}'::${sqlTypeLowered}`;
270
- columnToSet.default = column.default;
271
- }
272
- }
273
- }
274
- }
275
- columnsObject[name] = columnToSet;
276
- });
277
-
278
- primaryKeys.map((pk) => {
279
- const originalColumnNames = pk.columns.map((c) => c.name);
280
- const columnNames = pk.columns.map((c) => getColumnCasing(c, casing));
281
-
282
- let name = pk.getName();
283
- if (casing !== undefined) {
284
- for (let i = 0; i < originalColumnNames.length; i++) {
285
- name = name.replace(originalColumnNames[i], columnNames[i]);
286
- }
287
- }
288
-
289
- primaryKeysObject[name] = {
290
- name,
291
- columns: columnNames,
292
- };
293
- });
294
-
295
- uniqueConstraints?.map((unq) => {
296
- const columnNames = unq.columns.map((c) => getColumnCasing(c, casing));
297
-
298
- const name = unq.name ?? uniqueKeyName(table, columnNames);
299
-
300
- const existingUnique = uniqueConstraintObject[name];
301
- if (typeof existingUnique !== 'undefined') {
302
- console.log(
303
- `\n${
304
- withStyle.errorWarning(
305
- `We\'ve found duplicated unique constraint names in ${chalk.underline.blue(tableName)} table.
306
- The unique constraint ${chalk.underline.blue(name)} on the ${
307
- chalk.underline.blue(
308
- columnNames.join(','),
309
- )
310
- } columns is confilcting with a unique constraint name already defined for ${
311
- chalk.underline.blue(existingUnique.columns.join(','))
312
- } columns\n`,
313
- )
314
- }`,
315
- );
316
- process.exit(1);
317
- }
318
-
319
- uniqueConstraintObject[name] = {
320
- name: unq.name!,
321
- nullsNotDistinct: unq.nullsNotDistinct,
322
- columns: columnNames,
323
- };
324
- });
325
-
326
- const fks: ForeignKey[] = foreignKeys.map((fk) => {
327
- const tableFrom = tableName;
328
- const onDelete = fk.onDelete;
329
- const onUpdate = fk.onUpdate;
330
- const reference = fk.reference();
331
-
332
- const tableTo = getTableName(reference.foreignTable);
333
- // TODO: resolve issue with schema undefined/public for db push(or squasher)
334
- // getTableConfig(reference.foreignTable).schema || "public";
335
- const schemaTo = getTableConfig(reference.foreignTable).schema;
336
-
337
- const originalColumnsFrom = reference.columns.map((it) => it.name);
338
- const columnsFrom = reference.columns.map((it) => getColumnCasing(it, casing));
339
- const originalColumnsTo = reference.foreignColumns.map((it) => it.name);
340
- const columnsTo = reference.foreignColumns.map((it) => getColumnCasing(it, casing));
341
-
342
- let name = fk.getName();
343
- if (casing !== undefined) {
344
- for (let i = 0; i < originalColumnsFrom.length; i++) {
345
- name = name.replace(originalColumnsFrom[i], columnsFrom[i]);
346
- }
347
- for (let i = 0; i < originalColumnsTo.length; i++) {
348
- name = name.replace(originalColumnsTo[i], columnsTo[i]);
349
- }
350
- }
351
-
352
- return {
353
- name,
354
- tableFrom,
355
- tableTo,
356
- schemaTo,
357
- columnsFrom,
358
- columnsTo,
359
- onDelete,
360
- onUpdate,
361
- } as ForeignKey;
362
- });
363
-
364
- fks.forEach((it) => {
365
- foreignKeysObject[it.name] = it;
366
- });
367
-
368
- indexes.forEach((value) => {
369
- const columns = value.config.columns;
370
-
371
- let indexColumnNames: string[] = [];
372
- columns.forEach((it) => {
373
- if (is(it, SQL)) {
374
- if (typeof value.config.name === 'undefined') {
375
- console.log(
376
- `\n${
377
- withStyle.errorWarning(
378
- `Please specify an index name in ${getTableName(value.config.table)} table that has "${
379
- dialect.sqlToQuery(it).sql
380
- }" expression. We can generate index names for indexes on columns only; for expressions in indexes, you need to specify the name yourself.`,
381
- )
382
- }`,
383
- );
384
- process.exit(1);
385
- }
386
- }
387
- it = it as IndexedColumn;
388
- const name = getColumnCasing(it as IndexedColumn, casing);
389
- if (
390
- !is(it, SQL)
391
- && it.type! === 'PgVector'
392
- && typeof it.indexConfig!.opClass === 'undefined'
393
- ) {
394
- console.log(
395
- `\n${
396
- withStyle.errorWarning(
397
- `You are specifying an index on the ${
398
- chalk.blueBright(
399
- name,
400
- )
401
- } column inside the ${
402
- chalk.blueBright(
403
- tableName,
404
- )
405
- } table with the ${
406
- chalk.blueBright(
407
- 'vector',
408
- )
409
- } type without specifying an operator class. Vector extension doesn't have a default operator class, so you need to specify one of the available options. Here is a list of available op classes for the vector extension: [${
410
- vectorOps
411
- .map((it) => `${chalk.underline(`${it}`)}`)
412
- .join(', ')
413
- }].\n\nYou can specify it using current syntax: ${
414
- chalk.underline(
415
- `index("${value.config.name}").using("${value.config.method}", table.${name}.op("${
416
- vectorOps[0]
417
- }"))`,
418
- )
419
- }\n\nYou can check the "pg_vector" docs for more info: https://github.com/pgvector/pgvector?tab=readme-ov-file#indexing\n`,
420
- )
421
- }`,
422
- );
423
- process.exit(1);
424
- }
425
- indexColumnNames.push(name);
426
- });
427
-
428
- const name = value.config.name ? value.config.name : indexName(tableName, indexColumnNames);
429
-
430
- let indexColumns: IndexColumnType[] = columns.map(
431
- (it): IndexColumnType => {
432
- if (is(it, SQL)) {
433
- return {
434
- expression: dialect.sqlToQuery(it, 'indexes').sql,
435
- asc: true,
436
- isExpression: true,
437
- nulls: 'last',
438
- };
439
- } else {
440
- it = it as IndexedColumn;
441
- return {
442
- expression: getColumnCasing(it as IndexedColumn, casing),
443
- isExpression: false,
444
- asc: it.indexConfig?.order === 'asc',
445
- nulls: it.indexConfig?.nulls
446
- ? it.indexConfig?.nulls
447
- : it.indexConfig?.order === 'desc'
448
- ? 'first'
449
- : 'last',
450
- opclass: it.indexConfig?.opClass,
451
- };
452
- }
453
- },
454
- );
455
-
456
- // check for index names duplicates
457
- if (typeof indexesInSchema[schema ?? 'public'] !== 'undefined') {
458
- if (indexesInSchema[schema ?? 'public'].includes(name)) {
459
- console.log(
460
- `\n${
461
- withStyle.errorWarning(
462
- `We\'ve found duplicated index name across ${
463
- chalk.underline.blue(schema ?? 'public')
464
- } schema. Please rename your index in either the ${
465
- chalk.underline.blue(
466
- tableName,
467
- )
468
- } table or the table with the duplicated index name`,
469
- )
470
- }`,
471
- );
472
- process.exit(1);
473
- }
474
- indexesInSchema[schema ?? 'public'].push(name);
475
- } else {
476
- indexesInSchema[schema ?? 'public'] = [name];
477
- }
478
-
479
- indexesObject[name] = {
480
- name,
481
- columns: indexColumns,
482
- isUnique: value.config.unique ?? false,
483
- where: value.config.where ? dialect.sqlToQuery(value.config.where).sql : undefined,
484
- concurrently: value.config.concurrently ?? false,
485
- method: value.config.method ?? 'btree',
486
- with: value.config.with ?? {},
487
- };
488
- });
489
-
490
- policies.forEach((policy) => {
491
- const mappedTo = [];
492
-
493
- if (!policy.to) {
494
- mappedTo.push('public');
495
- } else {
496
- if (policy.to && typeof policy.to === 'string') {
497
- mappedTo.push(policy.to);
498
- } else if (policy.to && is(policy.to, PgRole)) {
499
- mappedTo.push(policy.to.name);
500
- } else if (policy.to && Array.isArray(policy.to)) {
501
- policy.to.forEach((it) => {
502
- if (typeof it === 'string') {
503
- mappedTo.push(it);
504
- } else if (is(it, PgRole)) {
505
- mappedTo.push(it.name);
506
- }
507
- });
508
- }
509
- }
510
-
511
- if (policiesObject[policy.name] !== undefined) {
512
- console.log(
513
- `\n${
514
- withStyle.errorWarning(
515
- `We\'ve found duplicated policy name across ${
516
- chalk.underline.blue(tableKey)
517
- } table. Please rename one of the policies with ${
518
- chalk.underline.blue(
519
- policy.name,
520
- )
521
- } name`,
522
- )
523
- }`,
524
- );
525
- process.exit(1);
526
- }
527
-
528
- policiesObject[policy.name] = {
529
- name: policy.name,
530
- as: policy.as?.toUpperCase() as Policy['as'] ?? 'PERMISSIVE',
531
- for: policy.for?.toUpperCase() as Policy['for'] ?? 'ALL',
532
- to: mappedTo.sort(),
533
- using: is(policy.using, SQL) ? dialect.sqlToQuery(policy.using).sql : undefined,
534
- withCheck: is(policy.withCheck, SQL) ? dialect.sqlToQuery(policy.withCheck).sql : undefined,
535
- };
536
- });
537
-
538
- checks.forEach((check, index) => {
539
- const tableKey = `"${schema ?? 'public'}"."${tableName}"`;
540
-
541
- // you can have multiple unnamed checks per table (using the default above)
542
- let defaultCheckName = `${tableName}_check`;
543
- let checkName = check.name ?? defaultCheckName;
544
- if (checksInTable[tableKey]?.includes(checkName) && checkName == defaultCheckName) {
545
- checkName += `_${index}`;
546
- }
547
-
548
- if (typeof checksInTable[`"${schema ?? 'public'}"."${tableName}"`] !== 'undefined') {
549
- if (checksInTable[`"${schema ?? 'public'}"."${tableName}"`].includes(checkName)) {
550
- console.log(
551
- `\n${
552
- withStyle.errorWarning(
553
- `We\'ve found duplicated check constraint name across ${
554
- chalk.underline.blue(
555
- schema ?? 'public',
556
- )
557
- } schema in ${
558
- chalk.underline.blue(
559
- tableName,
560
- )
561
- }. Please rename your check constraint in either the ${
562
- chalk.underline.blue(
563
- tableName,
564
- )
565
- } table or the table with the duplicated check contraint name`,
566
- )
567
- }`,
568
- );
569
- process.exit(1);
570
- }
571
- checksInTable[`"${schema ?? 'public'}"."${tableName}"`].push(checkName);
572
- } else {
573
- checksInTable[`"${schema ?? 'public'}"."${tableName}"`] = [checkName];
574
- }
575
-
576
- checksObject[checkName] = {
577
- name: check.name ?? '', // don't squash and include the default name
578
- value: dialect.sqlToQuery(check.value).sql,
579
- };
580
- });
581
-
582
- const tableKey = `${schema ?? 'public'}.${tableName}`;
583
-
584
- result[tableKey] = {
585
- name: tableName,
586
- schema: schema ?? '',
587
- columns: columnsObject,
588
- indexes: indexesObject,
589
- foreignKeys: foreignKeysObject,
590
- compositePrimaryKeys: primaryKeysObject,
591
- uniqueConstraints: uniqueConstraintObject,
592
- policies: policiesObject,
593
- checkConstraints: checksObject,
594
- isRLSEnabled: enableRLS,
595
- };
596
- }
597
-
598
- for (const policy of policies) {
599
- // @ts-ignore
600
- if (!policy._linkedTable) {
601
- console.log(
602
- `\n${
603
- withStyle.errorWarning(
604
- `"Policy ${policy.name} was skipped because it was not linked to any table. You should either include the policy in a table or use .link() on the policy to link it to any table you have. For more information, please check:`,
605
- )
606
- }`,
607
- );
608
- continue;
609
- }
610
-
611
- // @ts-ignore
612
- const tableConfig = getTableConfig(policy._linkedTable);
613
-
614
- const tableKey = `${tableConfig.schema ?? 'public'}.${tableConfig.name}`;
615
-
616
- const mappedTo = [];
617
-
618
- if (!policy.to) {
619
- mappedTo.push('public');
620
- } else {
621
- if (policy.to && typeof policy.to === 'string') {
622
- mappedTo.push(policy.to);
623
- } else if (policy.to && is(policy.to, PgRole)) {
624
- mappedTo.push(policy.to.name);
625
- } else if (policy.to && Array.isArray(policy.to)) {
626
- policy.to.forEach((it) => {
627
- if (typeof it === 'string') {
628
- mappedTo.push(it);
629
- } else if (is(it, PgRole)) {
630
- mappedTo.push(it.name);
631
- }
632
- });
633
- }
634
- }
635
-
636
- // add separate policies object, that will be only responsible for policy creation
637
- // but we would need to track if a policy was enabled for a specific table or not
638
- // enable only if jsonStatements for enable rls was not already there + filter it
639
-
640
- if (result[tableKey]?.policies[policy.name] !== undefined || policiesToReturn[policy.name] !== undefined) {
641
- console.log(
642
- `\n${
643
- withStyle.errorWarning(
644
- `We\'ve found duplicated policy name across ${
645
- chalk.underline.blue(tableKey)
646
- } table. Please rename one of the policies with ${
647
- chalk.underline.blue(
648
- policy.name,
649
- )
650
- } name`,
651
- )
652
- }`,
653
- );
654
- process.exit(1);
655
- }
656
-
657
- const mappedPolicy = {
658
- name: policy.name,
659
- as: policy.as?.toUpperCase() as Policy['as'] ?? 'PERMISSIVE',
660
- for: policy.for?.toUpperCase() as Policy['for'] ?? 'ALL',
661
- to: mappedTo.sort(),
662
- using: is(policy.using, SQL) ? dialect.sqlToQuery(policy.using).sql : undefined,
663
- withCheck: is(policy.withCheck, SQL) ? dialect.sqlToQuery(policy.withCheck).sql : undefined,
664
- };
665
-
666
- if (result[tableKey]) {
667
- result[tableKey].policies[policy.name] = mappedPolicy;
668
- } else {
669
- policiesToReturn[policy.name] = {
670
- ...mappedPolicy,
671
- schema: tableConfig.schema ?? 'public',
672
- on: `"${tableConfig.schema ?? 'public'}"."${tableConfig.name}"`,
673
- };
674
- }
675
- }
676
-
677
- for (const sequence of sequences) {
678
- const name = sequence.seqName!;
679
- if (typeof sequencesToReturn[`${sequence.schema ?? 'public'}.${name}`] === 'undefined') {
680
- const increment = stringFromIdentityProperty(sequence?.seqOptions?.increment) ?? '1';
681
- const minValue = stringFromIdentityProperty(sequence?.seqOptions?.minValue)
682
- ?? (parseFloat(increment) < 0 ? '-9223372036854775808' : '1');
683
- const maxValue = stringFromIdentityProperty(sequence?.seqOptions?.maxValue)
684
- ?? (parseFloat(increment) < 0 ? '-1' : '9223372036854775807');
685
- const startWith = stringFromIdentityProperty(sequence?.seqOptions?.startWith)
686
- ?? (parseFloat(increment) < 0 ? maxValue : minValue);
687
- const cache = stringFromIdentityProperty(sequence?.seqOptions?.cache) ?? '1';
688
-
689
- sequencesToReturn[`${sequence.schema ?? 'public'}.${name}`] = {
690
- name,
691
- schema: sequence.schema ?? 'public',
692
- increment,
693
- startWith,
694
- minValue,
695
- maxValue,
696
- cache,
697
- cycle: sequence.seqOptions?.cycle ?? false,
698
- };
699
- } else {
700
- // duplicate seq error
701
- }
702
- }
703
-
704
- for (const role of roles) {
705
- if (!(role as any)._existing) {
706
- rolesToReturn[role.name] = {
707
- name: role.name,
708
- createDb: (role as any).createDb === undefined ? false : (role as any).createDb,
709
- createRole: (role as any).createRole === undefined ? false : (role as any).createRole,
710
- inherit: (role as any).inherit === undefined ? true : (role as any).inherit,
711
- };
712
- }
713
- }
714
- const combinedViews = [...views, ...matViews];
715
- for (const view of combinedViews) {
716
- let viewName;
717
- let schema;
718
- let query;
719
- let selectedFields;
720
- let isExisting;
721
- let withOption;
722
- let tablespace;
723
- let using;
724
- let withNoData;
725
- let materialized: boolean = false;
726
-
727
- if (is(view, PgView)) {
728
- ({ name: viewName, schema, query, selectedFields, isExisting, with: withOption } = getViewConfig(view));
729
- } else {
730
- ({ name: viewName, schema, query, selectedFields, isExisting, with: withOption, tablespace, using, withNoData } =
731
- getMaterializedViewConfig(view));
732
-
733
- materialized = true;
734
- }
735
-
736
- const viewSchema = schema ?? 'public';
737
-
738
- const viewKey = `${viewSchema}.${viewName}`;
739
-
740
- const columnsObject: Record<string, Column> = {};
741
- const uniqueConstraintObject: Record<string, UniqueConstraint> = {};
742
-
743
- const existingView = resultViews[viewKey];
744
- if (typeof existingView !== 'undefined') {
745
- console.log(
746
- `\n${
747
- withStyle.errorWarning(
748
- `We\'ve found duplicated view name across ${
749
- chalk.underline.blue(schema ?? 'public')
750
- } schema. Please rename your view`,
751
- )
752
- }`,
753
- );
754
- process.exit(1);
755
- }
756
-
757
- for (const key in selectedFields) {
758
- if (is(selectedFields[key], PgColumn)) {
759
- const column = selectedFields[key];
760
-
761
- const notNull: boolean = column.notNull;
762
- const primaryKey: boolean = column.primary;
763
- const sqlTypeLowered = column.getSQLType().toLowerCase();
764
-
765
- const typeSchema = is(column, PgEnumColumn) ? column.enum.schema || 'public' : undefined;
766
- // const domainTypeSchema = is(column, PgDomainColumn) ? column.domain : undefined;
767
- const generated = column.generated;
768
- const identity = column.generatedIdentity;
769
-
770
- const increment = stringFromIdentityProperty(identity?.sequenceOptions?.increment) ?? '1';
771
- const minValue = stringFromIdentityProperty(identity?.sequenceOptions?.minValue)
772
- ?? (parseFloat(increment) < 0 ? minRangeForIdentityBasedOn(column.columnType) : '1');
773
- const maxValue = stringFromIdentityProperty(identity?.sequenceOptions?.maxValue)
774
- ?? (parseFloat(increment) < 0 ? '-1' : maxRangeForIdentityBasedOn(column.getSQLType()));
775
- const startWith = stringFromIdentityProperty(identity?.sequenceOptions?.startWith)
776
- ?? (parseFloat(increment) < 0 ? maxValue : minValue);
777
- const cache = stringFromIdentityProperty(identity?.sequenceOptions?.cache) ?? '1';
778
-
779
- const columnToSet: Column = {
780
- name: column.name,
781
- type: column.getSQLType(),
782
- typeSchema: typeSchema,
783
- primaryKey,
784
- notNull,
785
- generated: generated
786
- ? {
787
- as: is(generated.as, SQL)
788
- ? dialect.sqlToQuery(generated.as as SQL).sql
789
- : typeof generated.as === 'function'
790
- ? dialect.sqlToQuery(generated.as() as SQL).sql
791
- : (generated.as as any),
792
- type: 'stored',
793
- }
794
- : undefined,
795
- identity: identity
796
- ? {
797
- type: identity.type,
798
- name: identity.sequenceName ?? `${viewName}_${column.name}_seq`,
799
- schema: schema ?? 'public',
800
- increment,
801
- startWith,
802
- minValue,
803
- maxValue,
804
- cache,
805
- cycle: identity?.sequenceOptions?.cycle ?? false,
806
- }
807
- : undefined,
808
- };
809
-
810
- if (column.isUnique) {
811
- const existingUnique = uniqueConstraintObject[column.uniqueName!];
812
- if (typeof existingUnique !== 'undefined') {
813
- console.log(
814
- `\n${
815
- withStyle.errorWarning(
816
- `We\'ve found duplicated unique constraint names in ${chalk.underline.blue(viewName)} table.
817
- The unique constraint ${chalk.underline.blue(column.uniqueName)} on the ${
818
- chalk.underline.blue(
819
- column.name,
820
- )
821
- } column is confilcting with a unique constraint name already defined for ${
822
- chalk.underline.blue(existingUnique.columns.join(','))
823
- } columns\n`,
824
- )
825
- }`,
826
- );
827
- process.exit(1);
828
- }
829
- uniqueConstraintObject[column.uniqueName!] = {
830
- name: column.uniqueName!,
831
- nullsNotDistinct: column.uniqueType === 'not distinct',
832
- columns: [columnToSet.name],
833
- };
834
- }
835
-
836
- if (column.default !== undefined) {
837
- if (is(column.default, SQL)) {
838
- columnToSet.default = sqlToStr(column.default, casing);
839
- } else {
840
- if (typeof column.default === 'string') {
841
- columnToSet.default = `'${column.default}'`;
842
- } else {
843
- if (sqlTypeLowered === 'jsonb' || sqlTypeLowered === 'json') {
844
- columnToSet.default = `'${JSON.stringify(column.default)}'::${sqlTypeLowered}`;
845
- } else if (column.default instanceof Date) {
846
- if (sqlTypeLowered === 'date') {
847
- columnToSet.default = `'${column.default.toISOString().split('T')[0]}'`;
848
- } else if (sqlTypeLowered === 'timestamp') {
849
- columnToSet.default = `'${column.default.toISOString().replace('T', ' ').slice(0, 23)}'`;
850
- } else {
851
- columnToSet.default = `'${column.default.toISOString()}'`;
852
- }
853
- } else if (isPgArrayType(sqlTypeLowered) && Array.isArray(column.default)) {
854
- columnToSet.default = `'${buildArrayString(column.default, sqlTypeLowered)}'`;
855
- } else {
856
- // Should do for all types
857
- // columnToSet.default = `'${column.default}'::${sqlTypeLowered}`;
858
- columnToSet.default = column.default;
859
- }
860
- }
861
- }
862
- }
863
- columnsObject[column.name] = columnToSet;
864
- }
865
- }
866
-
867
- resultViews[viewKey] = {
868
- columns: columnsObject,
869
- definition: isExisting ? undefined : dialect.sqlToQuery(query!).sql,
870
- name: viewName,
871
- schema: viewSchema,
872
- isExisting,
873
- with: withOption,
874
- withNoData,
875
- materialized,
876
- tablespace,
877
- using,
878
- };
879
- }
880
-
881
- const domainsToReturn: Record<string, Domain> = domains.reduce<{ [key: string]: Domain }>((map, obj) => {
882
- // Process check constraints similar to tables
883
- const checksObject: Record<string, CheckConstraint> = {};
884
-
885
- obj.domainCheckConstraints?.forEach((checkConstraint, index) => {
886
- // Validate unique constraint names within domain
887
- // TODO check old domain name
888
- const domainKey = `"${obj.schema ?? 'public'}"."${obj.name}"`;
889
-
890
- // you can have multiple unnamed checks per domain (using the default above)
891
- let defaultCheckName = `${obj.domainName}_check`;
892
- let checkName = checkConstraint.name ?? defaultCheckName;
893
- if (checksInTable[domainKey]?.includes(checkName) && checkName == defaultCheckName) {
894
- checkName += `_${index}`;
895
- }
896
-
897
- checksInTable[domainKey] = checksInTable[domainKey]
898
- ? [...checksInTable[domainKey], checkName]
899
- : [checkName];
900
-
901
- checksObject[checkName] = {
902
- name: checkConstraint.name ?? '', // don't squash and include the default name
903
- value: dialect.sqlToQuery(checkConstraint.value).sql,
904
- };
905
- });
906
-
907
- const domainSchema = obj.schema || 'public';
908
- const key = `${domainSchema}.${obj.name}`;
909
- map[key] = {
910
- name: obj.domainName,
911
- schema: domainSchema,
912
- notNull: obj.domainNotNull,
913
- baseType: obj.domainType,
914
- defaultValue: obj.domainDefaultValue,
915
- checkConstraints: checksObject,
916
- };
917
-
918
- return map;
919
- }, {});
920
-
921
- const enumsToReturn: Record<string, Enum> = enums.reduce<{
922
- [key: string]: Enum;
923
- }>((map, obj) => {
924
- const enumSchema = obj.schema || 'public';
925
- const key = `${enumSchema}.${obj.enumName}`;
926
- map[key] = {
927
- name: obj.enumName,
928
- schema: enumSchema,
929
- values: obj.enumValues,
930
- };
931
- return map;
932
- }, {});
933
-
934
- const schemasObject = Object.fromEntries(
935
- schemas
936
- .filter((it) => {
937
- if (schemaFilter) {
938
- return schemaFilter.includes(it.schemaName) && it.schemaName !== 'public';
939
- } else {
940
- return it.schemaName !== 'public';
941
- }
942
- })
943
- .map((it) => [it.schemaName, it.schemaName]),
944
- );
945
-
946
- return {
947
- version: snapshotVersion,
948
- dialect: 'postgresql',
949
- tables: result,
950
- domains: domainsToReturn,
951
- enums: enumsToReturn,
952
- schemas: schemasObject,
953
- sequences: sequencesToReturn,
954
- roles: rolesToReturn,
955
- policies: policiesToReturn,
956
- views: resultViews,
957
- _meta: {
958
- schemas: {},
959
- tables: {},
960
- columns: {},
961
- },
962
- };
963
- };
964
-
965
- const trimChar = (str: string, char: string) => {
966
- let start = 0;
967
- let end = str.length;
968
-
969
- while (start < end && str[start] === char) ++start;
970
- while (end > start && str[end - 1] === char) --end;
971
-
972
- // this.toString() due to ava deep equal issue with String { "value" }
973
- return start > 0 || end < str.length ? str.substring(start, end) : str.toString();
974
- };
975
-
976
- function prepareRoles(entities?: {
977
- roles: boolean | {
978
- provider?: string | undefined;
979
- include?: string[] | undefined;
980
- exclude?: string[] | undefined;
981
- };
982
- }) {
983
- let useRoles: boolean = false;
984
- const includeRoles: string[] = [];
985
- const excludeRoles: string[] = [];
986
-
987
- if (entities && entities.roles) {
988
- if (typeof entities.roles === 'object') {
989
- if (entities.roles.provider) {
990
- if (entities.roles.provider === 'supabase') {
991
- excludeRoles.push(...[
992
- 'anon',
993
- 'authenticator',
994
- 'authenticated',
995
- 'service_role',
996
- 'supabase_auth_admin',
997
- 'supabase_storage_admin',
998
- 'dashboard_user',
999
- 'supabase_admin',
1000
- ]);
1001
- } else if (entities.roles.provider === 'neon') {
1002
- excludeRoles.push(...['authenticated', 'anonymous']);
1003
- }
1004
- }
1005
- if (entities.roles.include) {
1006
- includeRoles.push(...entities.roles.include);
1007
- }
1008
- if (entities.roles.exclude) {
1009
- excludeRoles.push(...entities.roles.exclude);
1010
- }
1011
- } else {
1012
- useRoles = entities.roles;
1013
- }
1014
- }
1015
- return { useRoles, includeRoles, excludeRoles };
1016
- }
1017
-
1018
- export const fromDatabase = async (
1019
- db: DB,
1020
- tablesFilter: (table: string) => boolean = () => true,
1021
- schemaFilters: string[],
1022
- entities?: {
1023
- roles: boolean | {
1024
- provider?: string | undefined;
1025
- include?: string[] | undefined;
1026
- exclude?: string[] | undefined;
1027
- };
1028
- },
1029
- progressCallback?: (
1030
- stage: IntrospectStage,
1031
- count: number,
1032
- status: IntrospectStatus,
1033
- ) => void,
1034
- tsSchema?: PgSchemaInternal,
1035
- ): Promise<PgSchemaInternal> => {
1036
- const result: Record<string, Table> = {};
1037
- const views: Record<string, View> = {};
1038
- const policies: Record<string, Policy> = {};
1039
- const internals: PgKitInternals = { tables: {} };
1040
-
1041
- const where = schemaFilters.map((t) => `n.nspname = '${t}'`).join(' or ');
1042
-
1043
- const allTables = await db.query<{ table_schema: string; table_name: string; type: string; rls_enabled: boolean }>(
1044
- `SELECT
1045
- n.nspname AS table_schema,
1046
- c.relname AS table_name,
1047
- CASE
1048
- WHEN c.relkind = 'r' THEN 'table'
1049
- WHEN c.relkind = 'v' THEN 'view'
1050
- WHEN c.relkind = 'm' THEN 'materialized_view'
1051
- END AS type,
1052
- c.relrowsecurity AS rls_enabled
1053
- FROM
1054
- pg_catalog.pg_class c
1055
- JOIN
1056
- pg_catalog.pg_namespace n ON n.oid = c.relnamespace
1057
- WHERE
1058
- c.relkind IN ('r', 'v', 'm')
1059
- ${where === '' ? '' : ` AND ${where}`};`,
1060
- );
1061
-
1062
- const schemas = new Set(allTables.map((it) => it.table_schema));
1063
- schemas.delete('public');
1064
-
1065
- const allSchemas = await db.query<{
1066
- table_schema: string;
1067
- }>(`select s.nspname as table_schema
1068
- from pg_catalog.pg_namespace s
1069
- join pg_catalog.pg_user u on u.usesysid = s.nspowner
1070
- where nspname not in ('information_schema', 'pg_catalog', 'public')
1071
- and nspname not like 'pg_toast%'
1072
- and nspname not like 'pg_temp_%'
1073
- order by table_schema;`);
1074
-
1075
- allSchemas.forEach((item) => {
1076
- if (schemaFilters.includes(item.table_schema)) {
1077
- schemas.add(item.table_schema);
1078
- }
1079
- });
1080
-
1081
- let columnsCount = 0;
1082
- let indexesCount = 0;
1083
- let foreignKeysCount = 0;
1084
- let tableCount = 0;
1085
- let checksCount = 0;
1086
- let viewsCount = 0;
1087
-
1088
- const sequencesToReturn: Record<string, Sequence> = {};
1089
-
1090
- const seqWhere = schemaFilters.map((t) => `schemaname = '${t}'`).join(' or ');
1091
-
1092
- const allSequences = await db.query(
1093
- `select schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size from pg_sequences as seq${
1094
- seqWhere === '' ? '' : ` WHERE ${seqWhere}`
1095
- };`,
1096
- );
1097
-
1098
- for (const dbSeq of allSequences) {
1099
- const schemaName = dbSeq.schemaname;
1100
- const sequenceName = dbSeq.sequencename;
1101
- const startValue = stringFromDatabaseIdentityProperty(dbSeq.start_value);
1102
- const minValue = stringFromDatabaseIdentityProperty(dbSeq.min_value);
1103
- const maxValue = stringFromDatabaseIdentityProperty(dbSeq.max_value);
1104
- const incrementBy = stringFromDatabaseIdentityProperty(dbSeq.increment_by);
1105
- const cycle = dbSeq.cycle;
1106
- const cacheSize = stringFromDatabaseIdentityProperty(dbSeq.cache_size);
1107
- const key = `${schemaName}.${sequenceName}`;
1108
-
1109
- sequencesToReturn[key] = {
1110
- name: sequenceName,
1111
- schema: schemaName,
1112
- startWith: startValue,
1113
- minValue,
1114
- maxValue,
1115
- increment: incrementBy,
1116
- cycle,
1117
- cache: cacheSize,
1118
- };
1119
- }
1120
-
1121
- const whereDomains = schemaFilters.map((t) => `n.nspname = '${t}'`).join(' or ');
1122
-
1123
- const allDomainsConstraints = await db.query(
1124
- `SELECT
1125
- n.nspname AS schema,
1126
- t.typname AS name,
1127
- t.typbasetype::regtype AS base_type,
1128
- CASE
1129
- WHEN t.typbasetype = 'character varying'::regtype AND t.typtypmod <> -1
1130
- THEN t.typtypmod - 4
1131
- ELSE NULL
1132
- END AS varchar_length,
1133
- t.typnotnull AS not_null,
1134
- t.typdefault AS default_value,
1135
- c.conname AS constraint_name, -- Get constraint name
1136
- pg_get_constraintdef(c.oid) AS domain_constraint
1137
- FROM
1138
- pg_type t
1139
- JOIN pg_namespace n ON t.typnamespace = n.oid
1140
- LEFT JOIN pg_constraint c ON t.oid = c.contypid AND c.contype = 'c'
1141
- WHERE
1142
- t.typtype = 'd'
1143
- ${whereDomains === '' ? '' : ` AND (${whereDomains})`}
1144
- ORDER BY
1145
- schema, name;`,
1146
- );
1147
-
1148
- const domainsToReturn: Record<string, Domain> = {};
1149
-
1150
- for (const domainConstraint of allDomainsConstraints) {
1151
- const schemaName = domainConstraint.schema || 'public';
1152
- const key = `${schemaName}.${domainConstraint.name}`;
1153
- let baseType = domainConstraint.base_type;
1154
- if (baseType === 'character varying') {
1155
- baseType = 'varchar';
1156
-
1157
- if (domainConstraint.varchar_length) {
1158
- baseType += `(${domainConstraint.varchar_length})`;
1159
- }
1160
- }
1161
-
1162
- if (!domainsToReturn[key]) {
1163
- domainsToReturn[key] = {
1164
- name: domainConstraint.name,
1165
- schema: schemaName,
1166
- baseType: baseType,
1167
- notNull: domainConstraint.not_null,
1168
- defaultValue: domainConstraint.default_value,
1169
- };
1170
- }
1171
-
1172
- // Add the check constraint if present in this row
1173
- if (domainConstraint.constraint_name && domainConstraint.domain_constraint) {
1174
- if (!domainsToReturn[key].checkConstraints) {
1175
- domainsToReturn[key].checkConstraints = {};
1176
- }
1177
-
1178
- domainsToReturn[key].checkConstraints[domainConstraint.constraint_name] = {
1179
- name: domainConstraint.constraint_name,
1180
- value: domainConstraint.domain_constraint,
1181
- };
1182
- }
1183
- }
1184
-
1185
- const whereEnums = schemaFilters.map((t) => `n.nspname = '${t}'`).join(' or ');
1186
-
1187
- const allEnums = await db.query(
1188
- `select n.nspname as enum_schema,
1189
- t.typname as enum_name,
1190
- e.enumlabel as enum_value,
1191
- e.enumsortorder as sort_order
1192
- from pg_type t
1193
- join pg_enum e on t.oid = e.enumtypid
1194
- join pg_catalog.pg_namespace n ON n.oid = t.typnamespace
1195
- ${whereEnums === '' ? '' : ` WHERE ${whereEnums}`}
1196
- order by enum_schema, enum_name, sort_order;`,
1197
- );
1198
-
1199
- const enumsToReturn: Record<string, Enum> = {};
1200
-
1201
- for (const dbEnum of allEnums) {
1202
- const enumName = dbEnum.enum_name;
1203
- const enumValue = dbEnum.enum_value as string;
1204
- const enumSchema: string = dbEnum.enum_schema || 'public';
1205
- const key = `${enumSchema}.${enumName}`;
1206
-
1207
- if (enumsToReturn[key] !== undefined && enumsToReturn[key] !== null) {
1208
- enumsToReturn[key].values.push(enumValue);
1209
- } else {
1210
- enumsToReturn[key] = {
1211
- name: enumName,
1212
- values: [enumValue],
1213
- schema: enumSchema,
1214
- };
1215
- }
1216
- }
1217
- if (progressCallback) {
1218
- progressCallback('enums', Object.keys(enumsToReturn).length, 'done');
1219
- }
1220
-
1221
- const allRoles = await db.query<
1222
- { rolname: string; rolinherit: boolean; rolcreatedb: boolean; rolcreaterole: boolean }
1223
- >(
1224
- `SELECT rolname, rolinherit, rolcreatedb, rolcreaterole FROM pg_roles;`,
1225
- );
1226
-
1227
- const rolesToReturn: Record<string, Role> = {};
1228
-
1229
- const preparedRoles = prepareRoles(entities);
1230
-
1231
- if (
1232
- preparedRoles.useRoles || !(preparedRoles.includeRoles.length === 0 && preparedRoles.excludeRoles.length === 0)
1233
- ) {
1234
- for (const dbRole of allRoles) {
1235
- if (
1236
- preparedRoles.useRoles
1237
- ) {
1238
- rolesToReturn[dbRole.rolname] = {
1239
- createDb: dbRole.rolcreatedb,
1240
- createRole: dbRole.rolcreatedb,
1241
- inherit: dbRole.rolinherit,
1242
- name: dbRole.rolname,
1243
- };
1244
- } else {
1245
- if (preparedRoles.includeRoles.length === 0 && preparedRoles.excludeRoles.length === 0) continue;
1246
- if (
1247
- preparedRoles.includeRoles.includes(dbRole.rolname) && preparedRoles.excludeRoles.includes(dbRole.rolname)
1248
- ) continue;
1249
- if (preparedRoles.excludeRoles.includes(dbRole.rolname)) continue;
1250
- if (!preparedRoles.includeRoles.includes(dbRole.rolname)) continue;
1251
-
1252
- rolesToReturn[dbRole.rolname] = {
1253
- createDb: dbRole.rolcreatedb,
1254
- createRole: dbRole.rolcreaterole,
1255
- inherit: dbRole.rolinherit,
1256
- name: dbRole.rolname,
1257
- };
1258
- }
1259
- }
1260
- }
1261
-
1262
- const schemasForLinkedPoliciesInSchema = Object.values(tsSchema?.policies ?? {}).map((it) => it.schema!);
1263
-
1264
- const wherePolicies = [...schemaFilters, ...schemasForLinkedPoliciesInSchema]
1265
- .map((t) => `schemaname = '${t}'`)
1266
- .join(' or ');
1267
-
1268
- const policiesByTable: Record<string, Record<string, Policy>> = {};
1269
-
1270
- const allPolicies = await db.query<
1271
- {
1272
- schemaname: string;
1273
- tablename: string;
1274
- name: string;
1275
- as: string;
1276
- to: string;
1277
- for: string;
1278
- using: string;
1279
- withCheck: string;
1280
- }
1281
- >(`SELECT schemaname, tablename, policyname as name, permissive as "as", roles as to, cmd as for, qual as using, with_check as "withCheck" FROM pg_policies${
1282
- wherePolicies === '' ? '' : ` WHERE ${wherePolicies}`
1283
- };`);
1284
-
1285
- for (const dbPolicy of allPolicies) {
1286
- const { tablename, schemaname, to, withCheck, using, ...rest } = dbPolicy;
1287
- const tableForPolicy = policiesByTable[`${schemaname}.${tablename}`];
1288
-
1289
- const parsedTo = typeof to === 'string' ? to.slice(1, -1).split(',') : to;
1290
-
1291
- const parsedWithCheck = withCheck === null ? undefined : withCheck;
1292
- const parsedUsing = using === null ? undefined : using;
1293
-
1294
- if (tableForPolicy) {
1295
- tableForPolicy[dbPolicy.name] = { ...rest, to: parsedTo } as Policy;
1296
- } else {
1297
- policiesByTable[`${schemaname}.${tablename}`] = {
1298
- [dbPolicy.name]: { ...rest, to: parsedTo, withCheck: parsedWithCheck, using: parsedUsing } as Policy,
1299
- };
1300
- }
1301
-
1302
- if (tsSchema?.policies[dbPolicy.name]) {
1303
- policies[dbPolicy.name] = {
1304
- ...rest,
1305
- to: parsedTo,
1306
- withCheck: parsedWithCheck,
1307
- using: parsedUsing,
1308
- on: tsSchema?.policies[dbPolicy.name].on,
1309
- } as Policy;
1310
- }
1311
- }
1312
-
1313
- if (progressCallback) {
1314
- progressCallback(
1315
- 'policies',
1316
- Object.values(policiesByTable).reduce((total, innerRecord) => {
1317
- return total + Object.keys(innerRecord).length;
1318
- }, 0),
1319
- 'done',
1320
- );
1321
- }
1322
-
1323
- const sequencesInColumns: string[] = [];
1324
-
1325
- const all = allTables
1326
- .filter((it) => it.type === 'table')
1327
- .map((row) => {
1328
- return new Promise(async (res, rej) => {
1329
- const tableName = row.table_name as string;
1330
- if (!tablesFilter(tableName)) return res('');
1331
- tableCount += 1;
1332
- const tableSchema = row.table_schema;
1333
-
1334
- try {
1335
- const columnToReturn: Record<string, Column> = {};
1336
- const indexToReturn: Record<string, Index> = {};
1337
- const foreignKeysToReturn: Record<string, ForeignKey> = {};
1338
- const primaryKeys: Record<string, PrimaryKey> = {};
1339
- const uniqueConstrains: Record<string, UniqueConstraint> = {};
1340
- const checkConstraints: Record<string, CheckConstraint> = {};
1341
-
1342
- const tableResponse = await getColumnsInfoQuery({ schema: tableSchema, table: tableName, db });
1343
-
1344
- const tableConstraints = await db.query(
1345
- `SELECT c.column_name, c.data_type, constraint_type, constraint_name, constraint_schema
1346
- FROM information_schema.table_constraints tc
1347
- JOIN information_schema.constraint_column_usage AS ccu USING (constraint_schema, constraint_name)
1348
- JOIN information_schema.columns AS c ON c.table_schema = tc.constraint_schema
1349
- AND tc.table_name = c.table_name AND ccu.column_name = c.column_name
1350
- WHERE tc.table_name = '${tableName}' and constraint_schema = '${tableSchema}';`,
1351
- );
1352
-
1353
- const tableChecks = await db.query(`SELECT
1354
- tc.constraint_name,
1355
- tc.constraint_type,
1356
- pg_get_expr(con.conbin, con.conrelid) AS check_expression
1357
- FROM
1358
- information_schema.table_constraints AS tc
1359
- JOIN pg_constraint AS con
1360
- ON tc.constraint_name = con.conname
1361
- AND con.conrelid = (
1362
- SELECT oid
1363
- FROM pg_class
1364
- WHERE relname = tc.table_name
1365
- AND relnamespace = (
1366
- SELECT oid
1367
- FROM pg_namespace
1368
- WHERE nspname = tc.constraint_schema
1369
- )
1370
- )
1371
- WHERE
1372
- tc.table_name = '${tableName}'
1373
- AND tc.constraint_schema = '${tableSchema}'
1374
- AND tc.constraint_type = 'CHECK';`);
1375
-
1376
- columnsCount += tableResponse.length;
1377
- if (progressCallback) {
1378
- progressCallback('columns', columnsCount, 'fetching');
1379
- }
1380
-
1381
- const tableForeignKeys = await db.query(
1382
- `SELECT
1383
- con.contype AS constraint_type,
1384
- nsp.nspname AS constraint_schema,
1385
- con.conname AS constraint_name,
1386
- rel.relname AS table_name,
1387
- att.attname AS column_name,
1388
- fnsp.nspname AS foreign_table_schema,
1389
- frel.relname AS foreign_table_name,
1390
- fatt.attname AS foreign_column_name,
1391
- CASE con.confupdtype
1392
- WHEN 'a' THEN 'NO ACTION'
1393
- WHEN 'r' THEN 'RESTRICT'
1394
- WHEN 'n' THEN 'SET NULL'
1395
- WHEN 'c' THEN 'CASCADE'
1396
- WHEN 'd' THEN 'SET DEFAULT'
1397
- END AS update_rule,
1398
- CASE con.confdeltype
1399
- WHEN 'a' THEN 'NO ACTION'
1400
- WHEN 'r' THEN 'RESTRICT'
1401
- WHEN 'n' THEN 'SET NULL'
1402
- WHEN 'c' THEN 'CASCADE'
1403
- WHEN 'd' THEN 'SET DEFAULT'
1404
- END AS delete_rule
1405
- FROM
1406
- pg_catalog.pg_constraint con
1407
- JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid
1408
- JOIN pg_catalog.pg_namespace nsp ON nsp.oid = con.connamespace
1409
- LEFT JOIN pg_catalog.pg_attribute att ON att.attnum = ANY (con.conkey)
1410
- AND att.attrelid = con.conrelid
1411
- LEFT JOIN pg_catalog.pg_class frel ON frel.oid = con.confrelid
1412
- LEFT JOIN pg_catalog.pg_namespace fnsp ON fnsp.oid = frel.relnamespace
1413
- LEFT JOIN pg_catalog.pg_attribute fatt ON fatt.attnum = ANY (con.confkey)
1414
- AND fatt.attrelid = con.confrelid
1415
- WHERE
1416
- nsp.nspname = '${tableSchema}'
1417
- AND rel.relname = '${tableName}'
1418
- AND con.contype IN ('f');`,
1419
- );
1420
-
1421
- foreignKeysCount += tableForeignKeys.length;
1422
- if (progressCallback) {
1423
- progressCallback('fks', foreignKeysCount, 'fetching');
1424
- }
1425
- for (const fk of tableForeignKeys) {
1426
- // const tableFrom = fk.table_name;
1427
- const columnFrom: string = fk.column_name;
1428
- const tableTo = fk.foreign_table_name;
1429
- const columnTo: string = fk.foreign_column_name;
1430
- const schemaTo: string = fk.foreign_table_schema;
1431
- const foreignKeyName = fk.constraint_name;
1432
- const onUpdate = fk.update_rule?.toLowerCase();
1433
- const onDelete = fk.delete_rule?.toLowerCase();
1434
-
1435
- if (typeof foreignKeysToReturn[foreignKeyName] !== 'undefined') {
1436
- foreignKeysToReturn[foreignKeyName].columnsFrom.push(columnFrom);
1437
- foreignKeysToReturn[foreignKeyName].columnsTo.push(columnTo);
1438
- } else {
1439
- foreignKeysToReturn[foreignKeyName] = {
1440
- name: foreignKeyName,
1441
- tableFrom: tableName,
1442
- tableTo,
1443
- schemaTo,
1444
- columnsFrom: [columnFrom],
1445
- columnsTo: [columnTo],
1446
- onDelete,
1447
- onUpdate,
1448
- };
1449
- }
1450
-
1451
- foreignKeysToReturn[foreignKeyName].columnsFrom = [
1452
- ...new Set(foreignKeysToReturn[foreignKeyName].columnsFrom),
1453
- ];
1454
-
1455
- foreignKeysToReturn[foreignKeyName].columnsTo = [...new Set(foreignKeysToReturn[foreignKeyName].columnsTo)];
1456
- }
1457
-
1458
- const uniqueConstrainsRows = tableConstraints.filter((mapRow) => mapRow.constraint_type === 'UNIQUE');
1459
-
1460
- for (const unqs of uniqueConstrainsRows) {
1461
- // const tableFrom = fk.table_name;
1462
- const columnName: string = unqs.column_name;
1463
- const constraintName: string = unqs.constraint_name;
1464
-
1465
- if (typeof uniqueConstrains[constraintName] !== 'undefined') {
1466
- uniqueConstrains[constraintName].columns.push(columnName);
1467
- } else {
1468
- uniqueConstrains[constraintName] = {
1469
- columns: [columnName],
1470
- nullsNotDistinct: false,
1471
- name: constraintName,
1472
- };
1473
- }
1474
- }
1475
-
1476
- checksCount += tableChecks.length;
1477
- if (progressCallback) {
1478
- progressCallback('checks', checksCount, 'fetching');
1479
- }
1480
- for (const checks of tableChecks) {
1481
- // CHECK (((email)::text <> 'test@gmail.com'::text))
1482
- // Where (email) is column in table
1483
- const checkValue: string = checks.check_expression;
1484
- const constraintName: string = checks.constraint_name;
1485
-
1486
- checkConstraints[constraintName] = {
1487
- name: constraintName,
1488
- value: checkValue,
1489
- };
1490
- }
1491
-
1492
- for (const columnResponse of tableResponse) {
1493
- const columnName = columnResponse.column_name;
1494
- const columnAdditionalDT = columnResponse.additional_dt;
1495
- const columnDimensions = columnResponse.array_dimensions;
1496
- const enumType: string = columnResponse.enum_name;
1497
- let columnType: string = columnResponse.data_type;
1498
- const typeSchema = columnResponse.type_schema;
1499
- const defaultValueRes: string = columnResponse.column_default;
1500
-
1501
- const isGenerated = columnResponse.is_generated === 'ALWAYS';
1502
- const generationExpression = columnResponse.generation_expression;
1503
- const isIdentity = columnResponse.is_identity === 'YES';
1504
- const identityGeneration = columnResponse.identity_generation === 'ALWAYS' ? 'always' : 'byDefault';
1505
- const identityStart = columnResponse.identity_start;
1506
- const identityIncrement = columnResponse.identity_increment;
1507
- const identityMaximum = columnResponse.identity_maximum;
1508
- const identityMinimum = columnResponse.identity_minimum;
1509
- const identityCycle = columnResponse.identity_cycle === 'YES';
1510
- const identityName = columnResponse.seq_name;
1511
-
1512
- const primaryKey = tableConstraints.filter((mapRow) =>
1513
- columnName === mapRow.column_name && mapRow.constraint_type === 'PRIMARY KEY'
1514
- );
1515
-
1516
- const cprimaryKey = tableConstraints.filter((mapRow) => mapRow.constraint_type === 'PRIMARY KEY');
1517
-
1518
- if (cprimaryKey.length > 1) {
1519
- const tableCompositePkName = await db.query(
1520
- `SELECT conname AS primary_key
1521
- FROM pg_constraint join pg_class on (pg_class.oid = conrelid)
1522
- WHERE contype = 'p'
1523
- AND connamespace = $1::regnamespace
1524
- AND pg_class.relname = $2;`,
1525
- [tableSchema, tableName],
1526
- );
1527
- primaryKeys[tableCompositePkName[0].primary_key] = {
1528
- name: tableCompositePkName[0].primary_key,
1529
- columns: cprimaryKey.map((c: any) => c.column_name),
1530
- };
1531
- }
1532
-
1533
- let columnTypeMapped = columnType;
1534
-
1535
- // Set default to internal object
1536
- if (columnAdditionalDT === 'ARRAY') {
1537
- if (typeof internals.tables[tableName] === 'undefined') {
1538
- internals.tables[tableName] = {
1539
- columns: {
1540
- [columnName]: {
1541
- isArray: true,
1542
- dimensions: columnDimensions,
1543
- rawType: columnTypeMapped.substring(0, columnTypeMapped.length - 2),
1544
- },
1545
- },
1546
- };
1547
- } else {
1548
- if (typeof internals.tables[tableName]!.columns[columnName] === 'undefined') {
1549
- internals.tables[tableName]!.columns[columnName] = {
1550
- isArray: true,
1551
- dimensions: columnDimensions,
1552
- rawType: columnTypeMapped.substring(0, columnTypeMapped.length - 2),
1553
- };
1554
- }
1555
- }
1556
- }
1557
-
1558
- const defaultValue = defaultForColumn(columnResponse, internals, tableName);
1559
- if (
1560
- defaultValue === 'NULL'
1561
- || (defaultValueRes && defaultValueRes.startsWith('(') && defaultValueRes.endsWith(')'))
1562
- ) {
1563
- if (typeof internals!.tables![tableName] === 'undefined') {
1564
- internals!.tables![tableName] = {
1565
- columns: {
1566
- [columnName]: {
1567
- isDefaultAnExpression: true,
1568
- },
1569
- },
1570
- };
1571
- } else {
1572
- if (typeof internals!.tables![tableName]!.columns[columnName] === 'undefined') {
1573
- internals!.tables![tableName]!.columns[columnName] = {
1574
- isDefaultAnExpression: true,
1575
- };
1576
- } else {
1577
- internals!.tables![tableName]!.columns[columnName]!.isDefaultAnExpression = true;
1578
- }
1579
- }
1580
- }
1581
-
1582
- const isSerial = columnType === 'serial';
1583
-
1584
- if (columnTypeMapped.startsWith('numeric(')) {
1585
- columnTypeMapped = columnTypeMapped.replace(',', ', ');
1586
- }
1587
-
1588
- if (columnAdditionalDT === 'ARRAY') {
1589
- for (let i = 1; i < Number(columnDimensions); i++) {
1590
- columnTypeMapped += '[]';
1591
- }
1592
- }
1593
-
1594
- // TODO make this available for domain code above as well
1595
- columnTypeMapped = columnTypeMapped
1596
- .replace('character varying', 'varchar')
1597
- .replace(' without time zone', '')
1598
- // .replace("timestamp without time zone", "timestamp")
1599
- .replace('character', 'char');
1600
-
1601
- columnTypeMapped = trimChar(columnTypeMapped, '"');
1602
-
1603
- columnToReturn[columnName] = {
1604
- name: columnName,
1605
- type:
1606
- // First, check if it's a domain
1607
- (columnAdditionalDT === 'DOMAIN'
1608
- && domainsToReturn[`${typeSchema}.${enumType}`]?.name)
1609
- // filter vectors, but in future we should filter any extension that was installed by user
1610
- || columnAdditionalDT === 'USER-DEFINED'
1611
- && !['vector', 'geometry'].includes(enumType)
1612
- ? enumType
1613
- : columnTypeMapped,
1614
- typeSchema:
1615
- // Check if it's a domain first
1616
- domainsToReturn[`${typeSchema}.${enumType}`]?.schema
1617
- // If not, check for enums
1618
- || enumsToReturn[`${typeSchema}.${enumType}`] !== undefined
1619
- ? enumsToReturn[`${typeSchema}.${enumType}`].schema
1620
- : undefined,
1621
- primaryKey: primaryKey.length === 1 && cprimaryKey.length < 2,
1622
- // default: isSerial ? undefined : defaultValue,
1623
- notNull: columnResponse.is_nullable === 'NO',
1624
- generated: isGenerated
1625
- ? { as: generationExpression, type: 'stored' }
1626
- : undefined,
1627
- identity: isIdentity
1628
- ? {
1629
- type: identityGeneration,
1630
- name: identityName,
1631
- increment: stringFromDatabaseIdentityProperty(identityIncrement),
1632
- minValue: stringFromDatabaseIdentityProperty(identityMinimum),
1633
- maxValue: stringFromDatabaseIdentityProperty(identityMaximum),
1634
- startWith: stringFromDatabaseIdentityProperty(identityStart),
1635
- cache: sequencesToReturn[identityName]?.cache
1636
- ? sequencesToReturn[identityName]?.cache
1637
- : sequencesToReturn[`${tableSchema}.${identityName}`]?.cache
1638
- ? sequencesToReturn[`${tableSchema}.${identityName}`]?.cache
1639
- : undefined,
1640
- cycle: identityCycle,
1641
- schema: tableSchema,
1642
- }
1643
- : undefined,
1644
- };
1645
-
1646
- if (identityName && typeof identityName === 'string') {
1647
- // remove "" from sequence name
1648
- delete sequencesToReturn[
1649
- `${tableSchema}.${
1650
- identityName.startsWith('"') && identityName.endsWith('"') ? identityName.slice(1, -1) : identityName
1651
- }`
1652
- ];
1653
- delete sequencesToReturn[identityName];
1654
- }
1655
-
1656
- if (!isSerial && typeof defaultValue !== 'undefined') {
1657
- columnToReturn[columnName].default = defaultValue;
1658
- }
1659
- }
1660
-
1661
- const dbIndexes = await db.query(
1662
- `SELECT DISTINCT ON (t.relname, ic.relname, k.i) t.relname as table_name, ic.relname AS indexname,
1663
- k.i AS index_order,
1664
- i.indisunique as is_unique,
1665
- am.amname as method,
1666
- ic.reloptions as with,
1667
- coalesce(a.attname,
1668
- (('{' || pg_get_expr(
1669
- i.indexprs,
1670
- i.indrelid
1671
- )
1672
- || '}')::text[]
1673
- )[k.i]
1674
- ) AS column_name,
1675
- CASE
1676
- WHEN pg_get_expr(i.indexprs, i.indrelid) IS NOT NULL THEN 1
1677
- ELSE 0
1678
- END AS is_expression,
1679
- i.indoption[k.i-1] & 1 = 1 AS descending,
1680
- i.indoption[k.i-1] & 2 = 2 AS nulls_first,
1681
- pg_get_expr(
1682
- i.indpred,
1683
- i.indrelid
1684
- ) as where,
1685
- opc.opcname
1686
- FROM pg_class t
1687
- LEFT JOIN pg_index i ON t.oid = i.indrelid
1688
- LEFT JOIN pg_class ic ON ic.oid = i.indexrelid
1689
- CROSS JOIN LATERAL (SELECT unnest(i.indkey), generate_subscripts(i.indkey, 1) + 1) AS k(attnum, i)
1690
- LEFT JOIN pg_attribute AS a
1691
- ON i.indrelid = a.attrelid AND k.attnum = a.attnum
1692
- JOIN pg_namespace c on c.oid = t.relnamespace
1693
- LEFT JOIN pg_am AS am ON ic.relam = am.oid
1694
- JOIN pg_opclass opc ON opc.oid = ANY(i.indclass)
1695
- WHERE
1696
- c.nspname = '${tableSchema}' AND
1697
- t.relname = '${tableName}';`,
1698
- );
1699
-
1700
- const dbIndexFromConstraint = await db.query(
1701
- `SELECT
1702
- idx.indexrelname AS index_name,
1703
- idx.relname AS table_name,
1704
- schemaname,
1705
- CASE WHEN con.conname IS NOT NULL THEN 1 ELSE 0 END AS generated_by_constraint
1706
- FROM
1707
- pg_stat_user_indexes idx
1708
- LEFT JOIN
1709
- pg_constraint con ON con.conindid = idx.indexrelid
1710
- WHERE idx.relname = '${tableName}' and schemaname = '${tableSchema}'
1711
- group by index_name, table_name,schemaname, generated_by_constraint;`,
1712
- );
1713
-
1714
- const idxsInConsteraint = dbIndexFromConstraint.filter((it) => it.generated_by_constraint === 1).map((it) =>
1715
- it.index_name
1716
- );
1717
-
1718
- for (const dbIndex of dbIndexes) {
1719
- const indexName: string = dbIndex.indexname;
1720
- const indexColumnName: string = dbIndex.column_name;
1721
- const indexIsUnique = dbIndex.is_unique;
1722
- const indexMethod = dbIndex.method;
1723
- const indexWith: string[] = dbIndex.with;
1724
- const indexWhere: string = dbIndex.where;
1725
- const opclass: string = dbIndex.opcname;
1726
- const isExpression = dbIndex.is_expression === 1;
1727
-
1728
- const desc: boolean = dbIndex.descending;
1729
- const nullsFirst: boolean = dbIndex.nulls_first;
1730
-
1731
- const mappedWith: Record<string, string> = {};
1732
-
1733
- if (indexWith !== null) {
1734
- indexWith
1735
- // .slice(1, indexWith.length - 1)
1736
- // .split(",")
1737
- .forEach((it) => {
1738
- const splitted = it.split('=');
1739
- mappedWith[splitted[0]] = splitted[1];
1740
- });
1741
- }
1742
-
1743
- if (idxsInConsteraint.includes(indexName)) continue;
1744
-
1745
- if (typeof indexToReturn[indexName] !== 'undefined') {
1746
- indexToReturn[indexName].columns.push({
1747
- expression: indexColumnName,
1748
- asc: !desc,
1749
- nulls: nullsFirst ? 'first' : 'last',
1750
- opclass,
1751
- isExpression,
1752
- });
1753
- } else {
1754
- indexToReturn[indexName] = {
1755
- name: indexName,
1756
- columns: [
1757
- {
1758
- expression: indexColumnName,
1759
- asc: !desc,
1760
- nulls: nullsFirst ? 'first' : 'last',
1761
- opclass,
1762
- isExpression,
1763
- },
1764
- ],
1765
- isUnique: indexIsUnique,
1766
- // should not be a part of diff detects
1767
- concurrently: false,
1768
- method: indexMethod,
1769
- where: indexWhere === null ? undefined : indexWhere,
1770
- with: mappedWith,
1771
- };
1772
- }
1773
- }
1774
-
1775
- indexesCount += Object.keys(indexToReturn).length;
1776
- if (progressCallback) {
1777
- progressCallback('indexes', indexesCount, 'fetching');
1778
- }
1779
- result[`${tableSchema}.${tableName}`] = {
1780
- name: tableName,
1781
- schema: tableSchema !== 'public' ? tableSchema : '',
1782
- columns: columnToReturn,
1783
- indexes: indexToReturn,
1784
- foreignKeys: foreignKeysToReturn,
1785
- compositePrimaryKeys: primaryKeys,
1786
- uniqueConstraints: uniqueConstrains,
1787
- checkConstraints: checkConstraints,
1788
- policies: policiesByTable[`${tableSchema}.${tableName}`] ?? {},
1789
- isRLSEnabled: row.rls_enabled,
1790
- };
1791
- } catch (e) {
1792
- rej(e);
1793
- return;
1794
- }
1795
- res('');
1796
- });
1797
- });
1798
-
1799
- if (progressCallback) {
1800
- progressCallback('tables', tableCount, 'done');
1801
- }
1802
-
1803
- for await (const _ of all) {
1804
- }
1805
-
1806
- const allViews = allTables
1807
- .filter((it) => it.type === 'view' || it.type === 'materialized_view')
1808
- .map((row) => {
1809
- return new Promise(async (res, rej) => {
1810
- const viewName = row.table_name as string;
1811
- if (!tablesFilter(viewName)) return res('');
1812
- tableCount += 1;
1813
- const viewSchema = row.table_schema;
1814
-
1815
- try {
1816
- const columnToReturn: Record<string, Column> = {};
1817
-
1818
- const viewResponses = await getColumnsInfoQuery({ schema: viewSchema, table: viewName, db });
1819
-
1820
- for (const viewResponse of viewResponses) {
1821
- const columnName = viewResponse.column_name;
1822
- const columnAdditionalDT = viewResponse.additional_dt;
1823
- const columnDimensions = viewResponse.array_dimensions;
1824
- const enumType: string = viewResponse.enum_name;
1825
- let columnType: string = viewResponse.data_type;
1826
- const typeSchema = viewResponse.type_schema;
1827
- // const defaultValueRes: string = viewResponse.column_default;
1828
-
1829
- const isGenerated = viewResponse.is_generated === 'ALWAYS';
1830
- const generationExpression = viewResponse.generation_expression;
1831
- const isIdentity = viewResponse.is_identity === 'YES';
1832
- const identityGeneration = viewResponse.identity_generation === 'ALWAYS' ? 'always' : 'byDefault';
1833
- const identityStart = viewResponse.identity_start;
1834
- const identityIncrement = viewResponse.identity_increment;
1835
- const identityMaximum = viewResponse.identity_maximum;
1836
- const identityMinimum = viewResponse.identity_minimum;
1837
- const identityCycle = viewResponse.identity_cycle === 'YES';
1838
- const identityName = viewResponse.seq_name;
1839
- const defaultValueRes = viewResponse.column_default;
1840
-
1841
- const primaryKey = viewResponse.constraint_type === 'PRIMARY KEY';
1842
-
1843
- let columnTypeMapped = columnType;
1844
-
1845
- // Set default to internal object
1846
- if (columnAdditionalDT === 'ARRAY') {
1847
- if (typeof internals.tables[viewName] === 'undefined') {
1848
- internals.tables[viewName] = {
1849
- columns: {
1850
- [columnName]: {
1851
- isArray: true,
1852
- dimensions: columnDimensions,
1853
- rawType: columnTypeMapped.substring(0, columnTypeMapped.length - 2),
1854
- },
1855
- },
1856
- };
1857
- } else {
1858
- if (typeof internals.tables[viewName]!.columns[columnName] === 'undefined') {
1859
- internals.tables[viewName]!.columns[columnName] = {
1860
- isArray: true,
1861
- dimensions: columnDimensions,
1862
- rawType: columnTypeMapped.substring(0, columnTypeMapped.length - 2),
1863
- };
1864
- }
1865
- }
1866
- }
1867
-
1868
- const defaultValue = defaultForColumn(viewResponse, internals, viewName);
1869
- if (
1870
- defaultValue === 'NULL'
1871
- || (defaultValueRes && defaultValueRes.startsWith('(') && defaultValueRes.endsWith(')'))
1872
- ) {
1873
- if (typeof internals!.tables![viewName] === 'undefined') {
1874
- internals!.tables![viewName] = {
1875
- columns: {
1876
- [columnName]: {
1877
- isDefaultAnExpression: true,
1878
- },
1879
- },
1880
- };
1881
- } else {
1882
- if (typeof internals!.tables![viewName]!.columns[columnName] === 'undefined') {
1883
- internals!.tables![viewName]!.columns[columnName] = {
1884
- isDefaultAnExpression: true,
1885
- };
1886
- } else {
1887
- internals!.tables![viewName]!.columns[columnName]!.isDefaultAnExpression = true;
1888
- }
1889
- }
1890
- }
1891
-
1892
- const isSerial = columnType === 'serial';
1893
-
1894
- if (columnTypeMapped.startsWith('numeric(')) {
1895
- columnTypeMapped = columnTypeMapped.replace(',', ', ');
1896
- }
1897
-
1898
- if (columnAdditionalDT === 'ARRAY') {
1899
- for (let i = 1; i < Number(columnDimensions); i++) {
1900
- columnTypeMapped += '[]';
1901
- }
1902
- }
1903
-
1904
- columnTypeMapped = columnTypeMapped
1905
- .replace('character varying', 'varchar')
1906
- .replace(' without time zone', '')
1907
- // .replace("timestamp without time zone", "timestamp")
1908
- .replace('character', 'char');
1909
-
1910
- columnTypeMapped = trimChar(columnTypeMapped, '"');
1911
-
1912
- columnToReturn[columnName] = {
1913
- name: columnName,
1914
- type:
1915
- // filter vectors, but in future we should filter any extension that was installed by user
1916
- columnAdditionalDT === 'USER-DEFINED' && !['vector', 'geometry'].includes(enumType)
1917
- ? enumType
1918
- : columnTypeMapped,
1919
- typeSchema: enumsToReturn[`${typeSchema}.${enumType}`] !== undefined
1920
- ? enumsToReturn[`${typeSchema}.${enumType}`].schema
1921
- : undefined,
1922
- primaryKey: primaryKey,
1923
- notNull: viewResponse.is_nullable === 'NO',
1924
- generated: isGenerated ? { as: generationExpression, type: 'stored' } : undefined,
1925
- identity: isIdentity
1926
- ? {
1927
- type: identityGeneration,
1928
- name: identityName,
1929
- increment: stringFromDatabaseIdentityProperty(identityIncrement),
1930
- minValue: stringFromDatabaseIdentityProperty(identityMinimum),
1931
- maxValue: stringFromDatabaseIdentityProperty(identityMaximum),
1932
- startWith: stringFromDatabaseIdentityProperty(identityStart),
1933
- cache: sequencesToReturn[identityName]?.cache
1934
- ? sequencesToReturn[identityName]?.cache
1935
- : sequencesToReturn[`${viewSchema}.${identityName}`]?.cache
1936
- ? sequencesToReturn[`${viewSchema}.${identityName}`]?.cache
1937
- : undefined,
1938
- cycle: identityCycle,
1939
- schema: viewSchema,
1940
- }
1941
- : undefined,
1942
- };
1943
-
1944
- if (identityName) {
1945
- // remove "" from sequence name
1946
- delete sequencesToReturn[
1947
- `${viewSchema}.${
1948
- identityName.startsWith('"') && identityName.endsWith('"') ? identityName.slice(1, -1) : identityName
1949
- }`
1950
- ];
1951
- delete sequencesToReturn[identityName];
1952
- }
1953
-
1954
- if (!isSerial && typeof defaultValue !== 'undefined') {
1955
- columnToReturn[columnName].default = defaultValue;
1956
- }
1957
- }
1958
-
1959
- const [viewInfo] = await db.query<{
1960
- view_name: string;
1961
- schema_name: string;
1962
- definition: string;
1963
- tablespace_name: string | null;
1964
- options: string[] | null;
1965
- location: string | null;
1966
- }>(`
1967
- SELECT
1968
- c.relname AS view_name,
1969
- n.nspname AS schema_name,
1970
- pg_get_viewdef(c.oid, true) AS definition,
1971
- ts.spcname AS tablespace_name,
1972
- c.reloptions AS options,
1973
- pg_tablespace_location(ts.oid) AS location
1974
- FROM
1975
- pg_class c
1976
- JOIN
1977
- pg_namespace n ON c.relnamespace = n.oid
1978
- LEFT JOIN
1979
- pg_tablespace ts ON c.reltablespace = ts.oid
1980
- WHERE
1981
- (c.relkind = 'm' OR c.relkind = 'v')
1982
- AND n.nspname = '${viewSchema}'
1983
- AND c.relname = '${viewName}';`);
1984
-
1985
- const resultWith: { [key: string]: string | boolean | number } = {};
1986
- if (viewInfo.options) {
1987
- viewInfo.options.forEach((pair) => {
1988
- const splitted = pair.split('=');
1989
- const key = splitted[0];
1990
- const value = splitted[1];
1991
-
1992
- if (value === 'true') {
1993
- resultWith[key] = true;
1994
- } else if (value === 'false') {
1995
- resultWith[key] = false;
1996
- } else if (!isNaN(Number(value))) {
1997
- resultWith[key] = Number(value);
1998
- } else {
1999
- resultWith[key] = value;
2000
- }
2001
- });
2002
- }
2003
-
2004
- const definition = viewInfo.definition.replace(/\s+/g, ' ').replace(';', '').trim();
2005
- // { "check_option":"cascaded","security_barrier":true} -> // { "checkOption":"cascaded","securityBarrier":true}
2006
- const withOption = Object.values(resultWith).length
2007
- ? Object.fromEntries(Object.entries(resultWith).map(([key, value]) => [key.camelCase(), value]))
2008
- : undefined;
2009
-
2010
- const materialized = row.type === 'materialized_view';
2011
-
2012
- views[`${viewSchema}.${viewName}`] = {
2013
- name: viewName,
2014
- schema: viewSchema,
2015
- columns: columnToReturn,
2016
- isExisting: false,
2017
- definition: definition,
2018
- materialized: materialized,
2019
- with: withOption,
2020
- tablespace: viewInfo.tablespace_name ?? undefined,
2021
- };
2022
- } catch (e) {
2023
- rej(e);
2024
- return;
2025
- }
2026
- res('');
2027
- });
2028
- });
2029
-
2030
- viewsCount = allViews.length;
2031
-
2032
- for await (const _ of allViews) {
2033
- }
2034
-
2035
- if (progressCallback) {
2036
- progressCallback('columns', columnsCount, 'done');
2037
- progressCallback('indexes', indexesCount, 'done');
2038
- progressCallback('fks', foreignKeysCount, 'done');
2039
- progressCallback('checks', checksCount, 'done');
2040
- progressCallback('views', viewsCount, 'done');
2041
- }
2042
-
2043
- const schemasObject = Object.fromEntries([...schemas].map((it) => [it, it]));
2044
-
2045
- return {
2046
- version: snapshotVersion,
2047
- dialect: 'postgresql',
2048
- tables: result,
2049
- domains: domainsToReturn,
2050
- enums: enumsToReturn,
2051
- schemas: schemasObject,
2052
- sequences: sequencesToReturn,
2053
- roles: rolesToReturn,
2054
- policies,
2055
- views: views,
2056
- _meta: {
2057
- schemas: {},
2058
- tables: {},
2059
- columns: {},
2060
- },
2061
- internal: internals,
2062
- };
2063
- };
2064
-
2065
- const defaultForColumn = (column: any, internals: PgKitInternals, tableName: string) => {
2066
- const columnName = column.column_name;
2067
- const isArray = internals?.tables[tableName]?.columns[columnName]?.isArray ?? false;
2068
-
2069
- if (
2070
- column.column_default === null
2071
- || column.column_default === undefined
2072
- || column.data_type === 'serial'
2073
- || column.data_type === 'smallserial'
2074
- || column.data_type === 'bigserial'
2075
- ) {
2076
- return undefined;
2077
- }
2078
-
2079
- if (column.column_default.endsWith('[]')) {
2080
- column.column_default = column.column_default.slice(0, -2);
2081
- }
2082
-
2083
- // if (
2084
- // !['integer', 'smallint', 'bigint', 'double precision', 'real'].includes(column.data_type)
2085
- // ) {
2086
- column.column_default = column.column_default.replace(/::(.*?)(?<![^\w"])(?=$)/, '');
2087
- // }
2088
-
2089
- const columnDefaultAsString: string = column.column_default.toString();
2090
-
2091
- if (isArray) {
2092
- return `'{${
2093
- columnDefaultAsString
2094
- .slice(2, -2)
2095
- .split(/\s*,\s*/g)
2096
- .map((value) => {
2097
- if (['integer', 'smallint', 'bigint', 'double precision', 'real'].includes(column.data_type.slice(0, -2))) {
2098
- return value;
2099
- } else if (column.data_type.startsWith('timestamp')) {
2100
- return `${value}`;
2101
- } else if (column.data_type.slice(0, -2) === 'interval') {
2102
- return value.replaceAll('"', `\"`);
2103
- } else if (column.data_type.slice(0, -2) === 'boolean') {
2104
- return value === 't' ? 'true' : 'false';
2105
- } else if (['json', 'jsonb'].includes(column.data_type.slice(0, -2))) {
2106
- return JSON.stringify(JSON.stringify(JSON.parse(JSON.parse(value)), null, 0));
2107
- } else {
2108
- return `\"${value}\"`;
2109
- }
2110
- })
2111
- .join(',')
2112
- }}'`;
2113
- }
2114
-
2115
- if (['integer', 'smallint', 'bigint', 'double precision', 'real'].includes(column.data_type)) {
2116
- if (/^-?[\d.]+(?:e-?\d+)?$/.test(columnDefaultAsString)) {
2117
- return Number(columnDefaultAsString);
2118
- } else {
2119
- if (typeof internals!.tables![tableName] === 'undefined') {
2120
- internals!.tables![tableName] = {
2121
- columns: {
2122
- [columnName]: {
2123
- isDefaultAnExpression: true,
2124
- },
2125
- },
2126
- };
2127
- } else {
2128
- if (typeof internals!.tables![tableName]!.columns[columnName] === 'undefined') {
2129
- internals!.tables![tableName]!.columns[columnName] = {
2130
- isDefaultAnExpression: true,
2131
- };
2132
- } else {
2133
- internals!.tables![tableName]!.columns[columnName]!.isDefaultAnExpression = true;
2134
- }
2135
- }
2136
- return columnDefaultAsString;
2137
- }
2138
- } else if (column.data_type.includes('numeric')) {
2139
- // if numeric(1,1) and used '99' -> psql stores like '99'::numeric
2140
- return columnDefaultAsString.includes("'") ? columnDefaultAsString : `'${columnDefaultAsString}'`;
2141
- } else if (column.data_type === 'json' || column.data_type === 'jsonb') {
2142
- const jsonWithoutSpaces = JSON.stringify(JSON.parse(columnDefaultAsString.slice(1, -1)));
2143
- return `'${jsonWithoutSpaces}'::${column.data_type}`;
2144
- } else if (column.data_type === 'boolean') {
2145
- return column.column_default === 'true';
2146
- } else if (columnDefaultAsString === 'NULL') {
2147
- return `NULL`;
2148
- } else if (columnDefaultAsString.startsWith("'") && columnDefaultAsString.endsWith("'")) {
2149
- return columnDefaultAsString;
2150
- } else {
2151
- return `${columnDefaultAsString.replace(/\\/g, '`\\')}`;
2152
- }
2153
- };
2154
-
2155
- const getColumnsInfoQuery = ({ schema, table, db }: { schema: string; table: string; db: DB }) => {
2156
- return db.query(
2157
- `SELECT
2158
- a.attrelid::regclass::text AS table_name, -- Table, view, or materialized view name
2159
- a.attname AS column_name, -- Column name
2160
- CASE
2161
- WHEN NOT a.attisdropped THEN
2162
- CASE
2163
- WHEN a.attnotnull THEN 'NO'
2164
- ELSE 'YES'
2165
- END
2166
- ELSE NULL
2167
- END AS is_nullable, -- NULL or NOT NULL constraint
2168
- a.attndims AS array_dimensions, -- Array dimensions
2169
- CASE
2170
- WHEN a.atttypid = ANY ('{int,int8,int2}'::regtype[])
2171
- AND EXISTS (
2172
- SELECT FROM pg_attrdef ad
2173
- WHERE ad.adrelid = a.attrelid
2174
- AND ad.adnum = a.attnum
2175
- AND pg_get_expr(ad.adbin, ad.adrelid) = 'nextval('''
2176
- || pg_get_serial_sequence(a.attrelid::regclass::text, a.attname)::regclass || '''::regclass)'
2177
- )
2178
- THEN CASE a.atttypid
2179
- WHEN 'int'::regtype THEN 'serial'
2180
- WHEN 'int8'::regtype THEN 'bigserial'
2181
- WHEN 'int2'::regtype THEN 'smallserial'
2182
- END
2183
- ELSE format_type(a.atttypid, a.atttypmod)
2184
- END AS data_type, -- Column data type
2185
- -- ns.nspname AS type_schema, -- Schema name
2186
- pg_get_serial_sequence('"${schema}"."${table}"', a.attname)::regclass AS seq_name, -- Serial sequence (if any)
2187
- c.column_default, -- Column default value
2188
- c.data_type AS additional_dt, -- Data type from information_schema
2189
- c.udt_name AS enum_name, -- Enum type (if applicable)
2190
- c.is_generated, -- Is it a generated column?
2191
- c.generation_expression, -- Generation expression (if generated)
2192
- c.is_identity, -- Is it an identity column?
2193
- c.identity_generation, -- Identity generation strategy (ALWAYS or BY DEFAULT)
2194
- c.identity_start, -- Start value of identity column
2195
- c.identity_increment, -- Increment for identity column
2196
- c.identity_maximum, -- Maximum value for identity column
2197
- c.identity_minimum, -- Minimum value for identity column
2198
- c.identity_cycle, -- Does the identity column cycle?
2199
- enum_ns.nspname AS type_schema -- Schema of the enum type
2200
- FROM
2201
- pg_attribute a
2202
- JOIN
2203
- pg_class cls ON cls.oid = a.attrelid -- Join pg_class to get table/view/materialized view info
2204
- JOIN
2205
- pg_namespace ns ON ns.oid = cls.relnamespace -- Join namespace to get schema info
2206
- LEFT JOIN
2207
- information_schema.columns c ON c.column_name = a.attname
2208
- AND c.table_schema = ns.nspname
2209
- AND c.table_name = cls.relname -- Match schema and table/view name
2210
- LEFT JOIN
2211
- pg_type enum_t ON enum_t.oid = a.atttypid -- Join to get the type info
2212
- LEFT JOIN
2213
- pg_namespace enum_ns ON enum_ns.oid = enum_t.typnamespace -- Join to get the enum schema
2214
- WHERE
2215
- a.attnum > 0 -- Valid column numbers only
2216
- AND NOT a.attisdropped -- Skip dropped columns
2217
- AND cls.relkind IN ('r', 'v', 'm') -- Include regular tables ('r'), views ('v'), and materialized views ('m')
2218
- AND ns.nspname = '${schema}' -- Filter by schema
2219
- AND cls.relname = '${table}' -- Filter by table name
2220
- ORDER BY
2221
- a.attnum; -- Order by column number`,
2222
- );
2223
- };