@anthonyalayo/drizzle-kit 1.0.1 → 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.
- package/api.d.mts +2891 -0
- package/api.d.ts +2891 -0
- package/api.js +47534 -0
- package/api.mjs +47521 -0
- package/bin.cjs +80043 -0
- package/{src/index.ts → index-Z-1TKnbX.d.mts} +123 -141
- package/index-Z-1TKnbX.d.ts +308 -0
- package/index.d.mts +2 -0
- package/index.d.ts +2 -0
- package/index.js +32 -0
- package/index.mjs +7 -0
- package/package.json +2 -2
- package/utils.js +6056 -0
- package/utils.mjs +6032 -0
- package/.gitignore +0 -25
- package/build.dev.ts +0 -51
- package/build.ts +0 -112
- package/imports-checker/analyze.ts +0 -78
- package/imports-checker/checker.ts +0 -296
- package/imports-checker/grammar/grammar.ohm +0 -121
- package/imports-checker/grammar/grammar.ohm-bundle.d.ts +0 -164
- package/imports-checker/grammar/grammar.ohm-bundle.js +0 -753
- package/imports-checker/index.ts +0 -48
- package/patches/difflib@0.2.4.patch +0 -31
- package/src/@types/utils.ts +0 -58
- package/src/api.ts +0 -504
- package/src/cli/commands/_es5.ts +0 -2
- package/src/cli/commands/check.ts +0 -52
- package/src/cli/commands/drop.ts +0 -60
- package/src/cli/commands/introspect.ts +0 -772
- package/src/cli/commands/libSqlPushUtils.ts +0 -355
- package/src/cli/commands/migrate.ts +0 -1526
- package/src/cli/commands/mysqlIntrospect.ts +0 -53
- package/src/cli/commands/mysqlPushUtils.ts +0 -352
- package/src/cli/commands/mysqlUp.ts +0 -102
- package/src/cli/commands/pgIntrospect.ts +0 -55
- package/src/cli/commands/pgPushUtils.ts +0 -269
- package/src/cli/commands/pgUp.ts +0 -217
- package/src/cli/commands/push.ts +0 -637
- package/src/cli/commands/singlestoreIntrospect.ts +0 -53
- package/src/cli/commands/singlestorePushUtils.ts +0 -456
- package/src/cli/commands/singlestoreUp.ts +0 -1
- package/src/cli/commands/sqliteIntrospect.ts +0 -96
- package/src/cli/commands/sqlitePushUtils.ts +0 -322
- package/src/cli/commands/sqliteUp.ts +0 -52
- package/src/cli/commands/utils.ts +0 -858
- package/src/cli/connections.ts +0 -914
- package/src/cli/index.ts +0 -48
- package/src/cli/schema.ts +0 -794
- package/src/cli/selector-ui.ts +0 -38
- package/src/cli/utils.ts +0 -112
- package/src/cli/validations/cli.ts +0 -79
- package/src/cli/validations/common.ts +0 -193
- package/src/cli/validations/libsql.ts +0 -27
- package/src/cli/validations/mysql.ts +0 -61
- package/src/cli/validations/outputs.ts +0 -91
- package/src/cli/validations/postgres.ts +0 -83
- package/src/cli/validations/singlestore.ts +0 -61
- package/src/cli/validations/sqlite.ts +0 -104
- package/src/cli/validations/studio.ts +0 -24
- package/src/cli/views.ts +0 -658
- package/src/extensions/getTablesFilterByExtensions.ts +0 -16
- package/src/extensions/vector.ts +0 -10
- package/src/global.ts +0 -61
- package/src/introspect-mysql.ts +0 -1030
- package/src/introspect-pg.ts +0 -1435
- package/src/introspect-singlestore.ts +0 -913
- package/src/introspect-sqlite.ts +0 -533
- package/src/jsonDiffer.js +0 -882
- package/src/jsonStatements.ts +0 -3590
- package/src/loader.mjs +0 -57
- package/src/migrationPreparator.ts +0 -217
- package/src/schemaValidator.ts +0 -23
- package/src/serializer/index.ts +0 -142
- package/src/serializer/mysqlImports.ts +0 -38
- package/src/serializer/mysqlSchema.ts +0 -423
- package/src/serializer/mysqlSerializer.ts +0 -999
- package/src/serializer/pgImports.ts +0 -106
- package/src/serializer/pgSchema.ts +0 -937
- package/src/serializer/pgSerializer.ts +0 -2223
- package/src/serializer/singlestoreImports.ts +0 -38
- package/src/serializer/singlestoreSchema.ts +0 -257
- package/src/serializer/singlestoreSerializer.ts +0 -767
- package/src/serializer/sqliteImports.ts +0 -41
- package/src/serializer/sqliteSchema.ts +0 -352
- package/src/serializer/sqliteSerializer.ts +0 -931
- package/src/serializer/studio.ts +0 -694
- package/src/serializer/utils.ts +0 -45
- package/src/simulator.ts +0 -157
- package/src/snapshotsDiffer.ts +0 -4398
- package/src/sqlgenerator.ts +0 -4212
- package/src/statementCombiner.ts +0 -597
- package/src/utils/certs.ts +0 -44
- package/src/utils/words.ts +0 -1333
- package/src/utils.ts +0 -370
- package/tests/bin.test.ts +0 -58
- package/tests/cli/d1http.config.ts +0 -12
- package/tests/cli/drizzle.config.ts +0 -9
- package/tests/cli/durable-sqlite.config.ts +0 -7
- package/tests/cli/expo.config.ts +0 -7
- package/tests/cli/postgres.config.ts +0 -13
- package/tests/cli/postgres2.config.ts +0 -17
- package/tests/cli/schema.ts +0 -1
- package/tests/cli/turso.config.ts +0 -10
- package/tests/cli-export.test.ts +0 -79
- package/tests/cli-generate.test.ts +0 -259
- package/tests/cli-migrate.test.ts +0 -103
- package/tests/cli-push.test.ts +0 -126
- package/tests/common.ts +0 -15
- package/tests/indexes/common.ts +0 -21
- package/tests/indexes/pg.test.ts +0 -245
- package/tests/introspect/libsql.test.ts +0 -35
- package/tests/introspect/mysql.test.ts +0 -319
- package/tests/introspect/pg.test.ts +0 -894
- package/tests/introspect/singlestore.test.ts +0 -275
- package/tests/introspect/sqlite.test.ts +0 -122
- package/tests/libsql-checks.test.ts +0 -308
- package/tests/libsql-statements.test.ts +0 -989
- package/tests/libsql-views.test.ts +0 -218
- package/tests/migrate/libsq-schema.ts +0 -6
- package/tests/migrate/libsql-migrate.test.ts +0 -58
- package/tests/migrate/migrations/0000_little_blizzard.sql +0 -4
- package/tests/migrate/migrations/0001_nebulous_storm.sql +0 -10
- package/tests/migrate/migrations/meta/0000_snapshot.json +0 -40
- package/tests/migrate/migrations/meta/0001_snapshot.json +0 -40
- package/tests/migrate/migrations/meta/_journal.json +0 -20
- package/tests/mysql-checks.test.ts +0 -291
- package/tests/mysql-generated.test.ts +0 -1290
- package/tests/mysql-schemas.test.ts +0 -156
- package/tests/mysql-views.test.ts +0 -553
- package/tests/mysql.test.ts +0 -863
- package/tests/pg-array.test.ts +0 -368
- package/tests/pg-checks.test.ts +0 -329
- package/tests/pg-columns.test.ts +0 -484
- package/tests/pg-domains.test.ts +0 -306
- package/tests/pg-enums.test.ts +0 -751
- package/tests/pg-generated.test.ts +0 -529
- package/tests/pg-identity.test.ts +0 -447
- package/tests/pg-schemas.test.ts +0 -105
- package/tests/pg-sequences.test.ts +0 -298
- package/tests/pg-tables.test.ts +0 -1021
- package/tests/pg-views.test.ts +0 -1929
- package/tests/push/common.ts +0 -58
- package/tests/push/libsql.test.ts +0 -1400
- package/tests/push/mysql-push.test.ts +0 -345
- package/tests/push/mysql.test.ts +0 -788
- package/tests/push/pg.test.ts +0 -4341
- package/tests/push/singlestore-push.test.ts +0 -894
- package/tests/push/singlestore.test.ts +0 -444
- package/tests/push/sqlite.test.ts +0 -1613
- package/tests/rls/pg-policy.test.ts +0 -1656
- package/tests/rls/pg-role.test.ts +0 -234
- package/tests/schemaDiffer.ts +0 -2852
- package/tests/singlestore-generated.test.ts +0 -1290
- package/tests/singlestore-schemas.test.ts +0 -155
- package/tests/singlestore.test.ts +0 -978
- package/tests/sqlite-checks.test.ts +0 -308
- package/tests/sqlite-columns.test.ts +0 -1049
- package/tests/sqlite-generated.test.ts +0 -1726
- package/tests/sqlite-tables.test.ts +0 -637
- package/tests/sqlite-views.test.ts +0 -218
- package/tests/statements-combiner/libsql-statements-combiner.test.ts +0 -1812
- package/tests/statements-combiner/singlestore-statements-combiner.test.ts +0 -882
- package/tests/statements-combiner/sqlite-statements-combiner.test.ts +0 -1211
- package/tests/test/sqlite.test.ts +0 -39
- package/tests/testsinglestore.ts +0 -29
- package/tests/validations.test.ts +0 -869
- package/tests/wrap-param.test.ts +0 -16
- package/tsconfig.build.json +0 -4
- package/tsconfig.cli-types.json +0 -9
- package/tsconfig.json +0 -28
- 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
|
-
};
|