@dbml/connector 3.7.4 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/connectors/bigqueryConnector.d.ts +3 -0
- package/dist/connectors/bigqueryConnector.js +276 -0
- package/dist/connectors/connector.d.ts +1 -1
- package/dist/connectors/connector.js +9 -3
- package/dist/connectors/mssqlConnector.js +76 -74
- package/dist/connectors/postgresConnector.js +19 -13
- package/dist/connectors/snowflakeConnector.d.ts +3 -0
- package/dist/connectors/snowflakeConnector.js +292 -0
- package/dist/connectors/types.d.ts +11 -1
- package/dist/utils/credential-loader.d.ts +3 -0
- package/dist/utils/credential-loader.js +53 -0
- package/dist/utils/helpers.d.ts +7 -0
- package/dist/utils/helpers.js +50 -0
- package/dist/utils/parseSchema.d.ts +10 -0
- package/dist/utils/parseSchema.js +59 -0
- package/package.json +5 -3
- package/src/connectors/bigqueryConnector.ts +389 -0
- package/src/connectors/connector.ts +9 -3
- package/src/connectors/mssqlConnector.ts +76 -74
- package/src/connectors/postgresConnector.ts +19 -13
- package/src/connectors/snowflakeConnector.ts +370 -0
- package/src/connectors/types.ts +12 -0
- package/src/ddl_samples/bigquery.sql +71 -0
- package/src/ddl_samples/snowflake.sql +131 -0
- package/src/utils/credential-loader.ts +64 -0
- package/src/utils/helpers.ts +73 -0
- package/src/utils/parseSchema.ts +60 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchSchemaJson = fetchSchemaJson;
|
|
4
|
+
const bigquery_1 = require("@google-cloud/bigquery");
|
|
5
|
+
const credential_loader_1 = require("../utils/credential-loader");
|
|
6
|
+
const helpers_1 = require("../utils/helpers");
|
|
7
|
+
async function connectBigQuery(credential) {
|
|
8
|
+
const client = new bigquery_1.BigQuery({
|
|
9
|
+
projectId: credential.projectId,
|
|
10
|
+
credentials: {
|
|
11
|
+
private_key: credential.credentials.privateKey,
|
|
12
|
+
client_email: credential.credentials.clientEmail,
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
try {
|
|
16
|
+
const query = `SELECT CURRENT_DATE() as today`;
|
|
17
|
+
await client.query(query);
|
|
18
|
+
return client;
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
if (error instanceof Error) {
|
|
22
|
+
throw new Error(`BigQuery connection error: ${error.message}`);
|
|
23
|
+
}
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function getDbDefault(columnDefault, defaultValueType) {
|
|
28
|
+
if (defaultValueType === null) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
if (defaultValueType === 'string') {
|
|
32
|
+
// bigquery store the double qoutes and quotes at the end and the beginning of the string: "string" or 'string'
|
|
33
|
+
return { value: columnDefault.slice(1, -1).replaceAll("'", "\'"), type: defaultValueType };
|
|
34
|
+
}
|
|
35
|
+
return { value: columnDefault, type: defaultValueType };
|
|
36
|
+
}
|
|
37
|
+
function generateField(row) {
|
|
38
|
+
const { columnName, isNullable, dataType, columnDefault, columnDefaultType, columnDescription, } = row;
|
|
39
|
+
const fieldDefaultValue = getDbDefault(columnDefault, columnDefaultType);
|
|
40
|
+
const field = {
|
|
41
|
+
name: columnName,
|
|
42
|
+
not_null: isNullable === 'NO',
|
|
43
|
+
type: {
|
|
44
|
+
type_name: dataType,
|
|
45
|
+
schemaName: null,
|
|
46
|
+
},
|
|
47
|
+
note: {
|
|
48
|
+
value: columnDescription || '',
|
|
49
|
+
},
|
|
50
|
+
dbdefault: fieldDefaultValue,
|
|
51
|
+
increment: false, // bigquery does not support native auto-increment
|
|
52
|
+
};
|
|
53
|
+
return field;
|
|
54
|
+
}
|
|
55
|
+
async function generateTablesAndFields(client, projectId, datasetId) {
|
|
56
|
+
const tableName = `${projectId}.${datasetId}`;
|
|
57
|
+
const query = `
|
|
58
|
+
with
|
|
59
|
+
table_comments as (
|
|
60
|
+
select table_schema, table_name, option_value as description
|
|
61
|
+
from ${tableName}.INFORMATION_SCHEMA.TABLE_OPTIONS
|
|
62
|
+
where option_name = 'description'
|
|
63
|
+
),
|
|
64
|
+
column_comments as (
|
|
65
|
+
select column_name, table_schema, table_name, description
|
|
66
|
+
from ${tableName}.INFORMATION_SCHEMA.COLUMN_FIELD_PATHS
|
|
67
|
+
where column_name = field_path
|
|
68
|
+
)
|
|
69
|
+
select
|
|
70
|
+
t.table_schema as tableSchema,
|
|
71
|
+
t.table_name as tableName,
|
|
72
|
+
(select description from table_comments tc where tc.table_name = t.table_name and tc.table_schema = t.table_schema) as tableDescription,
|
|
73
|
+
(
|
|
74
|
+
select description
|
|
75
|
+
from column_comments cc
|
|
76
|
+
where cc.table_name = c.table_name and cc.table_schema = c.table_schema and cc.column_name = c.column_name
|
|
77
|
+
) as columnDescription,
|
|
78
|
+
c.column_name as columnName,
|
|
79
|
+
c.is_nullable as isNullable,
|
|
80
|
+
c.data_type as dataType,
|
|
81
|
+
c.column_default as columnDefault,
|
|
82
|
+
CASE
|
|
83
|
+
WHEN SAFE_CAST(c.column_default AS FLOAT64) IS NOT NULL THEN 'number'
|
|
84
|
+
WHEN SAFE_CAST(c.column_default AS BOOLEAN) IS NOT NULL THEN 'boolean'
|
|
85
|
+
-- string value is enclosed in single quotes, double quotes
|
|
86
|
+
WHEN REGEXP_CONTAINS(c.column_default, r"^['\\"].*['\\"]$") THEN 'string'
|
|
87
|
+
WHEN c.column_default = "NULL" THEN NULL
|
|
88
|
+
ELSE 'expression'
|
|
89
|
+
END AS columnDefaultType
|
|
90
|
+
from ${tableName}.INFORMATION_SCHEMA.TABLES t
|
|
91
|
+
join ${tableName}.INFORMATION_SCHEMA.COLUMNS c
|
|
92
|
+
on
|
|
93
|
+
t.table_schema = c.table_schema
|
|
94
|
+
and t.table_name = c.table_name
|
|
95
|
+
where t.table_type = 'BASE TABLE'
|
|
96
|
+
order by t.table_name, c.ordinal_position;
|
|
97
|
+
`;
|
|
98
|
+
const [queryResult] = await client.query({
|
|
99
|
+
query,
|
|
100
|
+
params: {
|
|
101
|
+
// stringRegex: STRING_REGEX,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
const tableMap = {};
|
|
105
|
+
const fieldMap = {};
|
|
106
|
+
queryResult.forEach((row) => {
|
|
107
|
+
const { tableSchema, tableName, tableComment } = row;
|
|
108
|
+
const key = (0, helpers_1.getTableSchemaKey)(tableSchema, tableName);
|
|
109
|
+
if (!tableMap[key]) {
|
|
110
|
+
tableMap[key] = {
|
|
111
|
+
name: tableName,
|
|
112
|
+
note: { value: tableComment || '' },
|
|
113
|
+
schemaName: tableSchema,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
if (!fieldMap[key]) {
|
|
117
|
+
fieldMap[key] = [];
|
|
118
|
+
}
|
|
119
|
+
const field = generateField(row);
|
|
120
|
+
fieldMap[key].push(field);
|
|
121
|
+
});
|
|
122
|
+
return {
|
|
123
|
+
tables: Object.values(tableMap),
|
|
124
|
+
fieldMap,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
async function generateIndexes(client, projectId, datasetId) {
|
|
128
|
+
const tableName = `${projectId}.${datasetId}`;
|
|
129
|
+
const query = `
|
|
130
|
+
select
|
|
131
|
+
s.index_schema as indexSchema,
|
|
132
|
+
s.table_name as tableName,
|
|
133
|
+
s.index_name as indexName,
|
|
134
|
+
string_agg(sc.index_column_name, ', ') as indexColumnNames
|
|
135
|
+
from ${tableName}.INFORMATION_SCHEMA.SEARCH_INDEXES s
|
|
136
|
+
join ${tableName}.INFORMATION_SCHEMA.SEARCH_INDEX_COLUMNS sc
|
|
137
|
+
on
|
|
138
|
+
s.index_schema = sc.index_schema
|
|
139
|
+
and s.table_name = sc.table_name
|
|
140
|
+
and s.index_name = sc.index_name
|
|
141
|
+
where sc.index_column_name = sc.index_field_path
|
|
142
|
+
group by s.index_schema, s.table_name, s.index_name
|
|
143
|
+
order by s.table_name, s.index_name;
|
|
144
|
+
`;
|
|
145
|
+
const [queryResult] = await client.query(query);
|
|
146
|
+
const indexMap = {};
|
|
147
|
+
queryResult.forEach((row) => {
|
|
148
|
+
const { indexSchema, tableName, indexName, indexColumnNames } = row;
|
|
149
|
+
const indexColumns = indexColumnNames.split(', ').map((column) => {
|
|
150
|
+
return {
|
|
151
|
+
type: 'column',
|
|
152
|
+
value: column,
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
const index = {
|
|
156
|
+
name: indexName,
|
|
157
|
+
type: '',
|
|
158
|
+
columns: [...indexColumns],
|
|
159
|
+
};
|
|
160
|
+
const key = (0, helpers_1.getTableSchemaKey)(indexSchema, tableName);
|
|
161
|
+
if (indexMap[key]) {
|
|
162
|
+
indexMap[key].push(index);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
indexMap[key] = [index];
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
return indexMap;
|
|
169
|
+
}
|
|
170
|
+
async function generateTableConstraints(client, projectId, datasetId) {
|
|
171
|
+
const tableName = `${projectId}.${datasetId}`;
|
|
172
|
+
const query = `
|
|
173
|
+
select
|
|
174
|
+
tc.table_schema as tableSchema,
|
|
175
|
+
tc.table_name as tableName,
|
|
176
|
+
tc.constraint_name as constraintName,
|
|
177
|
+
string_agg(kcu.column_name, ', ') as columnNames,
|
|
178
|
+
count(kcu.column_name) as columnCount,
|
|
179
|
+
from ${tableName}.INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
|
|
180
|
+
join ${tableName}.INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
|
|
181
|
+
on
|
|
182
|
+
tc.table_schema = kcu.table_schema
|
|
183
|
+
and tc.table_name = kcu.table_name
|
|
184
|
+
and tc.constraint_name = kcu.constraint_name
|
|
185
|
+
where tc.constraint_type = 'PRIMARY KEY'
|
|
186
|
+
group by tc.table_schema, tc.table_name, tc.constraint_name
|
|
187
|
+
order by tc.table_name, tc.constraint_name;
|
|
188
|
+
`;
|
|
189
|
+
const [queryResult] = await client.query(query);
|
|
190
|
+
const inlinePrimaryKeyList = queryResult.filter((row) => row.columnCount && row.columnCount === 1);
|
|
191
|
+
const compositePrimaryKeyList = queryResult.filter((row) => row.columnCount && row.columnCount > 1);
|
|
192
|
+
const inlinePrimaryKeyMap = {};
|
|
193
|
+
inlinePrimaryKeyList.forEach((inlineKey) => {
|
|
194
|
+
const { tableSchema, tableName, columnNames } = inlineKey;
|
|
195
|
+
const key = (0, helpers_1.getTableSchemaKey)(tableSchema, tableName);
|
|
196
|
+
if (!inlinePrimaryKeyMap[key]) {
|
|
197
|
+
inlinePrimaryKeyMap[key] = {};
|
|
198
|
+
}
|
|
199
|
+
if (!inlinePrimaryKeyMap[key][columnNames]) {
|
|
200
|
+
inlinePrimaryKeyMap[key][columnNames] = {};
|
|
201
|
+
}
|
|
202
|
+
inlinePrimaryKeyMap[key][columnNames].pk = true;
|
|
203
|
+
});
|
|
204
|
+
const compositePrimaryKeyMap = {};
|
|
205
|
+
compositePrimaryKeyList.forEach((compositeKey) => {
|
|
206
|
+
const { tableSchema, tableName, constraintName, columnNames } = compositeKey;
|
|
207
|
+
const indexColumns = columnNames.split(', ').map((column) => {
|
|
208
|
+
return {
|
|
209
|
+
type: 'column',
|
|
210
|
+
value: column,
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
const index = {
|
|
214
|
+
name: constraintName,
|
|
215
|
+
type: '',
|
|
216
|
+
columns: [...indexColumns],
|
|
217
|
+
pk: true,
|
|
218
|
+
};
|
|
219
|
+
const key = (0, helpers_1.getTableSchemaKey)(tableSchema, tableName);
|
|
220
|
+
if (compositePrimaryKeyMap[key]) {
|
|
221
|
+
compositePrimaryKeyMap[key].push(index);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
compositePrimaryKeyMap[key] = [index];
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
return {
|
|
228
|
+
inlinePrimaryKeyMap,
|
|
229
|
+
compositePrimaryKeyMap,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
async function validateDatasetIdList(client, datasetIdList) {
|
|
233
|
+
const [datasets] = await client.getDatasets();
|
|
234
|
+
const rawDatasetIdList = datasets.map((dataset) => dataset.id).filter((id) => id);
|
|
235
|
+
// If user does not specify which datasets to be fetched, we will fetch all datasets
|
|
236
|
+
if (datasetIdList.length === 0) {
|
|
237
|
+
return rawDatasetIdList;
|
|
238
|
+
}
|
|
239
|
+
return (0, helpers_1.getIntersection)(datasetIdList, rawDatasetIdList);
|
|
240
|
+
}
|
|
241
|
+
async function fetchSchemaJsonByDataset(client, projectId, datasetId) {
|
|
242
|
+
const res = await Promise.all([
|
|
243
|
+
generateTablesAndFields(client, projectId, datasetId),
|
|
244
|
+
generateIndexes(client, projectId, datasetId),
|
|
245
|
+
generateTableConstraints(client, projectId, datasetId),
|
|
246
|
+
]);
|
|
247
|
+
const { tables, fieldMap } = res[0];
|
|
248
|
+
const indexMap = res[1];
|
|
249
|
+
const { inlinePrimaryKeyMap, compositePrimaryKeyMap } = res[2];
|
|
250
|
+
return {
|
|
251
|
+
tables,
|
|
252
|
+
fields: fieldMap,
|
|
253
|
+
refs: [],
|
|
254
|
+
enums: [],
|
|
255
|
+
indexes: (0, helpers_1.mergeIndexDictionary)(indexMap, compositePrimaryKeyMap),
|
|
256
|
+
tableConstraints: inlinePrimaryKeyMap,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
async function fetchSchemaJson(keyFilename) {
|
|
260
|
+
const rawCredential = await (0, credential_loader_1.loadCredentialFromFile)(keyFilename);
|
|
261
|
+
const credentialJson = (0, credential_loader_1.parseBigQueryCredential)(rawCredential);
|
|
262
|
+
const client = await connectBigQuery(credentialJson);
|
|
263
|
+
const projectId = await client.getProjectId();
|
|
264
|
+
const datasetIdList = await validateDatasetIdList(client, credentialJson.datasets);
|
|
265
|
+
const querySchemaPromises = datasetIdList.map((dataset) => fetchSchemaJsonByDataset(client, projectId, dataset));
|
|
266
|
+
const databaseSchemaRes = await Promise.all(querySchemaPromises);
|
|
267
|
+
const res = databaseSchemaRes.reduce((acc, dbSchema) => {
|
|
268
|
+
const { tables, fields, indexes, tableConstraints } = dbSchema;
|
|
269
|
+
acc.tables = (0, helpers_1.mergeTables)(acc.tables, tables);
|
|
270
|
+
acc.fields = (0, helpers_1.mergeFieldDictionary)(acc.fields, fields);
|
|
271
|
+
acc.indexes = (0, helpers_1.mergeIndexDictionary)(acc.indexes, indexes);
|
|
272
|
+
acc.tableConstraints = (0, helpers_1.mergeTableConstraintDictionary)(acc.tableConstraints, tableConstraints);
|
|
273
|
+
return acc;
|
|
274
|
+
}, { tables: [], fields: {}, tableConstraints: {}, indexes: {}, refs: [], enums: [] });
|
|
275
|
+
return res;
|
|
276
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { DatabaseSchema } from './types';
|
|
2
|
-
declare const fetchSchemaJson: (connection: string,
|
|
2
|
+
declare const fetchSchemaJson: (connection: string, databaseType: string) => Promise<DatabaseSchema>;
|
|
3
3
|
export { fetchSchemaJson, };
|
|
@@ -4,16 +4,22 @@ exports.fetchSchemaJson = void 0;
|
|
|
4
4
|
const postgresConnector_1 = require("./postgresConnector");
|
|
5
5
|
const mssqlConnector_1 = require("./mssqlConnector");
|
|
6
6
|
const mysqlConnector_1 = require("./mysqlConnector");
|
|
7
|
-
const
|
|
8
|
-
|
|
7
|
+
const snowflakeConnector_1 = require("./snowflakeConnector");
|
|
8
|
+
const bigqueryConnector_1 = require("./bigqueryConnector");
|
|
9
|
+
const fetchSchemaJson = async (connection, databaseType) => {
|
|
10
|
+
switch (databaseType) {
|
|
9
11
|
case 'postgres':
|
|
10
12
|
return (0, postgresConnector_1.fetchSchemaJson)(connection);
|
|
11
13
|
case 'mssql':
|
|
12
14
|
return (0, mssqlConnector_1.fetchSchemaJson)(connection);
|
|
13
15
|
case 'mysql':
|
|
14
16
|
return (0, mysqlConnector_1.fetchSchemaJson)(connection);
|
|
17
|
+
case 'snowflake':
|
|
18
|
+
return (0, snowflakeConnector_1.fetchSchemaJson)(connection);
|
|
19
|
+
case 'bigquery':
|
|
20
|
+
return (0, bigqueryConnector_1.fetchSchemaJson)(connection);
|
|
15
21
|
default:
|
|
16
|
-
throw new Error(`Unsupported
|
|
22
|
+
throw new Error(`Unsupported database type: ${databaseType}`);
|
|
17
23
|
}
|
|
18
24
|
};
|
|
19
25
|
exports.fetchSchemaJson = fetchSchemaJson;
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.fetchSchemaJson = void 0;
|
|
7
7
|
/* eslint-disable camelcase */
|
|
8
8
|
const mssql_1 = __importDefault(require("mssql"));
|
|
9
|
+
const parseSchema_1 = require("../utils/parseSchema");
|
|
9
10
|
const MSSQL_DATE_TYPES = [
|
|
10
11
|
'date',
|
|
11
12
|
'datetime',
|
|
@@ -101,79 +102,75 @@ const generateField = (row) => {
|
|
|
101
102
|
note: column_comment ? { value: column_comment } : { value: '' },
|
|
102
103
|
};
|
|
103
104
|
};
|
|
104
|
-
const generateTablesFieldsAndEnums = async (client) => {
|
|
105
|
+
const generateTablesFieldsAndEnums = async (client, schemas) => {
|
|
105
106
|
const fields = {};
|
|
106
107
|
const enums = [];
|
|
107
108
|
const tablesAndFieldsSql = `
|
|
108
|
-
|
|
109
|
-
SELECT
|
|
110
|
-
s.name AS table_schema,
|
|
111
|
-
t.name AS table_name,
|
|
112
|
-
c.name AS column_name,
|
|
113
|
-
ty.name AS data_type,
|
|
114
|
-
c.max_length AS character_maximum_length,
|
|
115
|
-
c.precision AS numeric_precision,
|
|
116
|
-
c.scale AS numeric_scale,
|
|
117
|
-
c.is_identity AS identity_increment,
|
|
118
|
-
CASE
|
|
119
|
-
WHEN c.is_nullable = 1 THEN 'YES'
|
|
120
|
-
ELSE 'NO'
|
|
121
|
-
END AS is_nullable,
|
|
122
|
-
CASE
|
|
123
|
-
WHEN c.default_object_id = 0 THEN NULL
|
|
124
|
-
ELSE OBJECT_DEFINITION(c.default_object_id)
|
|
125
|
-
END AS column_default,
|
|
126
|
-
-- Fetching table comments
|
|
127
|
-
p.value AS table_comment,
|
|
128
|
-
ep.value AS column_comment
|
|
129
|
-
FROM
|
|
130
|
-
sys.tables t
|
|
131
|
-
JOIN
|
|
132
|
-
sys.schemas s ON t.schema_id = s.schema_id
|
|
133
|
-
JOIN
|
|
134
|
-
sys.columns c ON t.object_id = c.object_id
|
|
135
|
-
JOIN
|
|
136
|
-
sys.types ty ON c.user_type_id = ty.user_type_id
|
|
137
|
-
LEFT JOIN
|
|
138
|
-
sys.extended_properties p ON p.major_id = t.object_id
|
|
139
|
-
AND p.name = 'MS_Description'
|
|
140
|
-
AND p.minor_id = 0 -- Ensure minor_id is 0 for table comments
|
|
141
|
-
LEFT JOIN
|
|
142
|
-
sys.extended_properties ep ON ep.major_id = c.object_id
|
|
143
|
-
AND ep.minor_id = c.column_id
|
|
144
|
-
AND ep.name = 'MS_Description'
|
|
145
|
-
WHERE
|
|
146
|
-
t.type = 'U' -- User-defined tables
|
|
147
|
-
)
|
|
109
|
+
WITH tables_and_fields AS (
|
|
148
110
|
SELECT
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
111
|
+
s.name AS table_schema,
|
|
112
|
+
t.name AS table_name,
|
|
113
|
+
c.name AS column_name,
|
|
114
|
+
ty.name AS data_type,
|
|
115
|
+
c.max_length AS character_maximum_length,
|
|
116
|
+
c.precision AS numeric_precision,
|
|
117
|
+
c.scale AS numeric_scale,
|
|
118
|
+
c.is_identity AS identity_increment,
|
|
119
|
+
CASE
|
|
120
|
+
WHEN c.is_nullable = 1 THEN 'YES'
|
|
121
|
+
ELSE 'NO'
|
|
122
|
+
END AS is_nullable,
|
|
123
|
+
CASE
|
|
124
|
+
WHEN c.default_object_id = 0 THEN NULL
|
|
125
|
+
ELSE OBJECT_DEFINITION(c.default_object_id)
|
|
126
|
+
END AS column_default,
|
|
127
|
+
-- Fetching table comments
|
|
128
|
+
p.value AS table_comment,
|
|
129
|
+
ep.value AS column_comment
|
|
168
130
|
FROM
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
131
|
+
sys.tables t
|
|
132
|
+
JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
133
|
+
JOIN sys.columns c ON t.object_id = c.object_id
|
|
134
|
+
JOIN sys.types ty ON c.user_type_id = ty.user_type_id
|
|
135
|
+
LEFT JOIN sys.extended_properties p ON p.major_id = t.object_id
|
|
136
|
+
AND p.name = 'MS_Description'
|
|
137
|
+
AND p.minor_id = 0 -- Ensure minor_id is 0 for table comments
|
|
138
|
+
LEFT JOIN sys.extended_properties ep ON ep.major_id = c.object_id
|
|
139
|
+
AND ep.minor_id = c.column_id
|
|
140
|
+
AND ep.name = 'MS_Description'
|
|
141
|
+
WHERE
|
|
142
|
+
t.type = 'U' -- User-defined tables
|
|
143
|
+
)
|
|
144
|
+
SELECT
|
|
145
|
+
tf.table_schema,
|
|
146
|
+
tf.table_name,
|
|
147
|
+
tf.column_name,
|
|
148
|
+
tf.data_type,
|
|
149
|
+
tf.character_maximum_length,
|
|
150
|
+
tf.numeric_precision,
|
|
151
|
+
tf.numeric_scale,
|
|
152
|
+
tf.identity_increment,
|
|
153
|
+
tf.is_nullable,
|
|
154
|
+
tf.column_default,
|
|
155
|
+
tf.table_comment,
|
|
156
|
+
tf.column_comment,
|
|
157
|
+
cc.name AS check_constraint_name, -- Adding CHECK constraint name
|
|
158
|
+
cc.definition AS check_constraint_definition, -- Adding CHECK constraint definition
|
|
159
|
+
CASE
|
|
160
|
+
WHEN tf.column_default LIKE '((%))' THEN 'number'
|
|
161
|
+
WHEN tf.column_default LIKE '(''%'')' THEN 'string'
|
|
162
|
+
ELSE 'expression'
|
|
163
|
+
END AS default_type
|
|
164
|
+
FROM
|
|
165
|
+
tables_and_fields AS tf
|
|
166
|
+
LEFT JOIN sys.check_constraints cc
|
|
167
|
+
ON cc.parent_object_id = OBJECT_ID(tf.table_schema + '.' + tf.table_name)
|
|
168
|
+
AND cc.definition LIKE '%' + tf.column_name + '%' -- Ensure the constraint references the column
|
|
169
|
+
${(0, parseSchema_1.buildSchemaQuery)('tf.table_schema', schemas, 'WHERE')}
|
|
170
|
+
ORDER BY
|
|
171
|
+
tf.table_schema,
|
|
172
|
+
tf.table_name,
|
|
173
|
+
tf.column_name;
|
|
177
174
|
`;
|
|
178
175
|
const tablesAndFieldsResult = await client.query(tablesAndFieldsSql);
|
|
179
176
|
const tables = tablesAndFieldsResult.recordset.reduce((acc, row) => {
|
|
@@ -212,7 +209,7 @@ const generateTablesFieldsAndEnums = async (client) => {
|
|
|
212
209
|
enums,
|
|
213
210
|
};
|
|
214
211
|
};
|
|
215
|
-
const generateRefs = async (client) => {
|
|
212
|
+
const generateRefs = async (client, schemas) => {
|
|
216
213
|
const refs = [];
|
|
217
214
|
const refsListSql = `
|
|
218
215
|
SELECT
|
|
@@ -242,6 +239,7 @@ const generateRefs = async (client) => {
|
|
|
242
239
|
JOIN sys.tables AS t2 ON fk.referenced_object_id = t2.object_id
|
|
243
240
|
JOIN sys.schemas AS s2 ON t2.schema_id = s2.schema_id
|
|
244
241
|
WHERE s.name NOT IN ('sys', 'information_schema')
|
|
242
|
+
${(0, parseSchema_1.buildSchemaQuery)('s.name', schemas)}
|
|
245
243
|
ORDER BY
|
|
246
244
|
s.name,
|
|
247
245
|
t.name;
|
|
@@ -270,7 +268,7 @@ const generateRefs = async (client) => {
|
|
|
270
268
|
});
|
|
271
269
|
return refs;
|
|
272
270
|
};
|
|
273
|
-
const generateIndexes = async (client) => {
|
|
271
|
+
const generateIndexes = async (client, schemas) => {
|
|
274
272
|
const indexListSql = `
|
|
275
273
|
WITH user_tables AS (
|
|
276
274
|
SELECT
|
|
@@ -288,6 +286,7 @@ const generateIndexes = async (client) => {
|
|
|
288
286
|
),
|
|
289
287
|
index_info AS (
|
|
290
288
|
SELECT
|
|
289
|
+
SCHEMA_NAME(t.schema_id) AS table_schema, -- Add schema information
|
|
291
290
|
OBJECT_NAME(i.object_id) AS table_name,
|
|
292
291
|
i.name AS index_name,
|
|
293
292
|
i.is_unique,
|
|
@@ -335,8 +334,10 @@ const generateIndexes = async (client) => {
|
|
|
335
334
|
user_tables ut
|
|
336
335
|
LEFT JOIN
|
|
337
336
|
index_info ii ON ut.TABLE_NAME = ii.table_name
|
|
337
|
+
AND ut.TABLE_SCHEMA = ii.table_schema
|
|
338
338
|
WHERE
|
|
339
339
|
ii.columns IS NOT NULL
|
|
340
|
+
${(0, parseSchema_1.buildSchemaQuery)('ut.TABLE_SCHEMA', schemas)}
|
|
340
341
|
ORDER BY
|
|
341
342
|
ut.TABLE_NAME,
|
|
342
343
|
ii.constraint_type,
|
|
@@ -410,10 +411,11 @@ const generateIndexes = async (client) => {
|
|
|
410
411
|
};
|
|
411
412
|
};
|
|
412
413
|
const fetchSchemaJson = async (connection) => {
|
|
413
|
-
const
|
|
414
|
-
const
|
|
415
|
-
const
|
|
416
|
-
const
|
|
414
|
+
const { connectionString, schemas } = (0, parseSchema_1.parseConnectionString)(connection, 'odbc');
|
|
415
|
+
const client = await getValidatedClient(connectionString);
|
|
416
|
+
const tablesFieldsAndEnumsRes = generateTablesFieldsAndEnums(client, schemas);
|
|
417
|
+
const indexesRes = generateIndexes(client, schemas);
|
|
418
|
+
const refsRes = generateRefs(client, schemas);
|
|
417
419
|
const res = await Promise.all([
|
|
418
420
|
tablesFieldsAndEnumsRes,
|
|
419
421
|
indexesRes,
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.fetchSchemaJson = void 0;
|
|
4
4
|
/* eslint-disable camelcase */
|
|
5
5
|
const pg_1 = require("pg");
|
|
6
|
+
const parseSchema_1 = require("../utils/parseSchema");
|
|
6
7
|
const getValidatedClient = async (connection) => {
|
|
7
8
|
const client = new pg_1.Client(connection);
|
|
8
9
|
try {
|
|
@@ -78,7 +79,7 @@ const generateField = (row) => {
|
|
|
78
79
|
note: column_comment ? { value: column_comment } : { value: '' },
|
|
79
80
|
};
|
|
80
81
|
};
|
|
81
|
-
const generateTablesAndFields = async (client) => {
|
|
82
|
+
const generateTablesAndFields = async (client, schemas) => {
|
|
82
83
|
const fields = {};
|
|
83
84
|
const tablesAndFieldsSql = `
|
|
84
85
|
WITH comments AS (
|
|
@@ -129,6 +130,7 @@ const generateTablesAndFields = async (client) => {
|
|
|
129
130
|
WHERE
|
|
130
131
|
t.table_type = 'BASE TABLE'
|
|
131
132
|
AND t.table_schema NOT IN ('pg_catalog', 'information_schema')
|
|
133
|
+
${(0, parseSchema_1.buildSchemaQuery)('t.table_schema', schemas)}
|
|
132
134
|
ORDER BY
|
|
133
135
|
t.table_schema,
|
|
134
136
|
t.table_name,
|
|
@@ -157,7 +159,7 @@ const generateTablesAndFields = async (client) => {
|
|
|
157
159
|
fields,
|
|
158
160
|
};
|
|
159
161
|
};
|
|
160
|
-
const generateRawRefs = async (client) => {
|
|
162
|
+
const generateRawRefs = async (client, schemas) => {
|
|
161
163
|
const refs = [];
|
|
162
164
|
const refsListSql = `
|
|
163
165
|
SELECT
|
|
@@ -182,6 +184,7 @@ const generateRawRefs = async (client) => {
|
|
|
182
184
|
AND tc.table_schema = rc.constraint_schema
|
|
183
185
|
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
184
186
|
AND tc.table_schema NOT IN ('pg_catalog', 'information_schema')
|
|
187
|
+
${(0, parseSchema_1.buildSchemaQuery)('tc.table_schema', schemas)}
|
|
185
188
|
GROUP BY
|
|
186
189
|
tc.table_schema,
|
|
187
190
|
tc.table_name,
|
|
@@ -219,8 +222,7 @@ const generateRawRefs = async (client) => {
|
|
|
219
222
|
});
|
|
220
223
|
return refs;
|
|
221
224
|
};
|
|
222
|
-
const generateIndexes = async (client) => {
|
|
223
|
-
// const tableConstraints = {};
|
|
225
|
+
const generateIndexes = async (client, schemas) => {
|
|
224
226
|
const indexListSql = `
|
|
225
227
|
WITH user_tables AS (
|
|
226
228
|
SELECT
|
|
@@ -233,6 +235,7 @@ const generateIndexes = async (client) => {
|
|
|
233
235
|
),
|
|
234
236
|
index_info AS (
|
|
235
237
|
SELECT
|
|
238
|
+
t.relnamespace::regnamespace::text AS table_schema,
|
|
236
239
|
t.relname AS table_name,
|
|
237
240
|
i.relname AS index_name,
|
|
238
241
|
ix.indisunique AS is_unique,
|
|
@@ -257,7 +260,7 @@ const generateIndexes = async (client) => {
|
|
|
257
260
|
AND t.relname NOT LIKE 'pg_%'
|
|
258
261
|
AND t.relname NOT LIKE 'sql_%'
|
|
259
262
|
GROUP BY
|
|
260
|
-
t.relname, i.relname, ix.indisunique, ix.indisprimary, am.amname, ix.indexprs, ix.indrelid
|
|
263
|
+
t.relnamespace, t.relname, i.relname, ix.indisunique, ix.indisprimary, am.amname, ix.indexprs, ix.indrelid
|
|
261
264
|
)
|
|
262
265
|
SELECT
|
|
263
266
|
ut.tableschema AS table_schema,
|
|
@@ -268,12 +271,13 @@ const generateIndexes = async (client) => {
|
|
|
268
271
|
ii.index_type,
|
|
269
272
|
ii.columns,
|
|
270
273
|
ii.expressions,
|
|
271
|
-
ii.constraint_type --
|
|
274
|
+
ii.constraint_type -- Retained constraint type here
|
|
272
275
|
FROM
|
|
273
276
|
user_tables ut
|
|
274
277
|
LEFT JOIN
|
|
275
|
-
index_info ii ON ut.tablename = ii.table_name
|
|
278
|
+
index_info ii ON ut.tableschema = ii.table_schema AND ut.tablename = ii.table_name
|
|
276
279
|
WHERE ii.columns IS NOT NULL
|
|
280
|
+
${(0, parseSchema_1.buildSchemaQuery)('ut.tableschema', schemas)}
|
|
277
281
|
ORDER BY
|
|
278
282
|
ut.tablename,
|
|
279
283
|
ii.constraint_type,
|
|
@@ -349,7 +353,7 @@ const generateIndexes = async (client) => {
|
|
|
349
353
|
tableConstraints,
|
|
350
354
|
};
|
|
351
355
|
};
|
|
352
|
-
const generateRawEnums = async (client) => {
|
|
356
|
+
const generateRawEnums = async (client, schemas) => {
|
|
353
357
|
const enumListSql = `
|
|
354
358
|
SELECT
|
|
355
359
|
n.nspname AS schema_name,
|
|
@@ -362,6 +366,7 @@ const generateRawEnums = async (client) => {
|
|
|
362
366
|
pg_type t ON e.enumtypid = t.oid
|
|
363
367
|
JOIN
|
|
364
368
|
pg_namespace n ON t.typnamespace = n.oid
|
|
369
|
+
${(0, parseSchema_1.buildSchemaQuery)('n.nspname', schemas, 'WHERE')}
|
|
365
370
|
ORDER BY
|
|
366
371
|
schema_name,
|
|
367
372
|
enum_type,
|
|
@@ -386,11 +391,12 @@ const generateRawEnums = async (client) => {
|
|
|
386
391
|
return Object.values(enums);
|
|
387
392
|
};
|
|
388
393
|
const fetchSchemaJson = async (connection) => {
|
|
389
|
-
const
|
|
390
|
-
const
|
|
391
|
-
const
|
|
392
|
-
const
|
|
393
|
-
const
|
|
394
|
+
const { connectionString, schemas } = (0, parseSchema_1.parseConnectionString)(connection, 'jdbc');
|
|
395
|
+
const client = await getValidatedClient(connectionString);
|
|
396
|
+
const tablesAndFieldsRes = generateTablesAndFields(client, schemas);
|
|
397
|
+
const indexesRes = generateIndexes(client, schemas);
|
|
398
|
+
const refsRes = generateRawRefs(client, schemas);
|
|
399
|
+
const enumsRes = generateRawEnums(client, schemas);
|
|
394
400
|
const res = await Promise.all([
|
|
395
401
|
tablesAndFieldsRes,
|
|
396
402
|
indexesRes,
|