@dbml/connector 5.6.0-alpha.2 → 6.0.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/.babelrc +13 -0
- package/dist/connectors/bigquery/index.js +291 -0
- package/dist/connectors/connector.js +28 -0
- package/dist/connectors/mssql/index.js +486 -0
- package/dist/connectors/mysql/index.js +454 -0
- package/dist/connectors/oracle/constraints.js +112 -0
- package/dist/connectors/oracle/index.js +68 -0
- package/dist/connectors/oracle/indexes.js +65 -0
- package/dist/connectors/oracle/refs.js +67 -0
- package/dist/connectors/oracle/tables.js +280 -0
- package/dist/connectors/oracle/utils.js +27 -0
- package/dist/connectors/postgres/index.js +517 -0
- package/dist/connectors/snowflake/index.js +311 -0
- package/dist/connectors/types.js +2 -0
- package/dist/index.js +38 -3739
- package/dist/utils/credential-loader.js +58 -0
- package/dist/utils/helpers.js +61 -0
- package/dist/utils/parseSchema.js +59 -0
- package/eslint.config.ts +1 -2
- package/jest.config.ts +18 -0
- package/package.json +27 -9
- package/src/connectors/mysql/index.ts +1 -1
- package/dist/index.mjs +0 -10359
- package/vite.config.ts +0 -53
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
//
|
|
3
|
+
// Description:
|
|
4
|
+
// Snowflake info schema lacks reference direction for relationships.
|
|
5
|
+
// Constraints and uniqueness cannot be auto-generated from Snowflake info schema.
|
|
6
|
+
// Snowflake does not support ENUMs.
|
|
7
|
+
// Snowflake does not support indexes for standard tables.
|
|
8
|
+
//
|
|
9
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.fetchSchemaJson = void 0;
|
|
14
|
+
const snowflake_sdk_1 = __importDefault(require("snowflake-sdk"));
|
|
15
|
+
const parseSchema_1 = require("../../utils/parseSchema");
|
|
16
|
+
const parseConnectionString = (connectionString) => {
|
|
17
|
+
const params = {};
|
|
18
|
+
const regex = /([^;=]+)=([^;]*)/g;
|
|
19
|
+
let match;
|
|
20
|
+
while ((match = regex.exec(connectionString)) !== null) {
|
|
21
|
+
params[match[1].trim()] = match[2].trim();
|
|
22
|
+
}
|
|
23
|
+
return params;
|
|
24
|
+
};
|
|
25
|
+
const connect = async (connection) => {
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
console.log('Attempting to connect...');
|
|
28
|
+
connection.connect((err) => {
|
|
29
|
+
if (err) {
|
|
30
|
+
reject(err);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
resolve();
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
const executeQuery = (connection, sqlText) => {
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
console.log('Executing query...');
|
|
41
|
+
connection.execute({
|
|
42
|
+
sqlText,
|
|
43
|
+
complete: (err, stmt, rows) => {
|
|
44
|
+
if (err) {
|
|
45
|
+
reject(err);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
console.log('Query executed successfully.');
|
|
49
|
+
if (rows) {
|
|
50
|
+
resolve(rows);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
const isLogLevel = (value) => {
|
|
58
|
+
return ['ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE'].includes(value);
|
|
59
|
+
};
|
|
60
|
+
const connectToSnowflake = async (config) => {
|
|
61
|
+
const logLevel = isLogLevel(config.LOG_LEVEL) ? config.LOG_LEVEL : 'INFO';
|
|
62
|
+
const isDebugMode = config.IS_DEBUG_MODE === 'true';
|
|
63
|
+
snowflake_sdk_1.default.configure({
|
|
64
|
+
logLevel,
|
|
65
|
+
additionalLogToConsole: isDebugMode,
|
|
66
|
+
});
|
|
67
|
+
// https://docs.snowflake.com/en/developer-guide/node-js/nodejs-driver-options#authentication-options
|
|
68
|
+
let authConfig = {};
|
|
69
|
+
switch (config.AUTHENTICATOR) {
|
|
70
|
+
case 'SNOWFLAKE_JWT':
|
|
71
|
+
// https://docs.snowflake.com/en/developer-guide/node-js/nodejs-driver-authenticate#label-nodejs-key-pair-authentication
|
|
72
|
+
// Support private key file
|
|
73
|
+
authConfig = {
|
|
74
|
+
privateKeyPath: config.PRIVATE_KEY_PATH,
|
|
75
|
+
privateKeyPass: config.PASSPHRASE,
|
|
76
|
+
};
|
|
77
|
+
break;
|
|
78
|
+
case 'SNOWFLAKE':
|
|
79
|
+
case undefined:
|
|
80
|
+
authConfig = {
|
|
81
|
+
password: config.PWD,
|
|
82
|
+
};
|
|
83
|
+
break;
|
|
84
|
+
default:
|
|
85
|
+
throw new Error('Unsupported authenticator');
|
|
86
|
+
}
|
|
87
|
+
// "SERVER=myaccount.snowflakecomputing.com;UID=myusername;PWD=mypassword;DATABASE=mydatabase;WAREHOUSE=mywarehouse;ROLE=myrole";
|
|
88
|
+
const connectionOptions = {
|
|
89
|
+
authenticator: config.AUTHENTICATOR,
|
|
90
|
+
account: config.SERVER,
|
|
91
|
+
username: config.UID,
|
|
92
|
+
database: config.DATABASE,
|
|
93
|
+
warehouse: config.WAREHOUSE,
|
|
94
|
+
sfRetryMaxLoginRetries: 3,
|
|
95
|
+
timeout: 10000,
|
|
96
|
+
...authConfig,
|
|
97
|
+
};
|
|
98
|
+
const connection = snowflake_sdk_1.default.createConnection(connectionOptions);
|
|
99
|
+
// Connect to Snowflake
|
|
100
|
+
await connect(connection);
|
|
101
|
+
// Execute the query
|
|
102
|
+
await executeQuery(connection, 'SELECT CURRENT_VERSION();');
|
|
103
|
+
return connection;
|
|
104
|
+
};
|
|
105
|
+
const convertQueryBoolean = (val) => val === 'YES';
|
|
106
|
+
const getFieldType = (data_type, character_maximum_length, numeric_precision, numeric_scale) => {
|
|
107
|
+
if (character_maximum_length) {
|
|
108
|
+
return `${data_type}(${character_maximum_length})`;
|
|
109
|
+
}
|
|
110
|
+
if (numeric_precision && numeric_scale) {
|
|
111
|
+
return `${data_type}(${numeric_precision},${numeric_scale})`;
|
|
112
|
+
}
|
|
113
|
+
return data_type;
|
|
114
|
+
};
|
|
115
|
+
const getDbdefault = (column_default, default_type) => {
|
|
116
|
+
if (default_type === 'string') {
|
|
117
|
+
return {
|
|
118
|
+
type: default_type,
|
|
119
|
+
value: column_default.slice(1, -1),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
type: default_type,
|
|
124
|
+
value: column_default,
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
const generateField = (row) => {
|
|
128
|
+
const { COLUMN_NAME: column_name, DATA_TYPE: data_type, CHARACTER_MAXIMUM_LENGTH: character_maximum_length, NUMERIC_PRECISION: numeric_precision, NUMERIC_SCALE: numeric_scale, IDENTITY_INCREMENT: identity_increment, IS_NULLABLE: is_nullable, COLUMN_DEFAULT: column_default, DEFAULT_TYPE: default_type, COLUMN_COMMENT: column_comment, } = row;
|
|
129
|
+
const dbdefault = column_default ? getDbdefault(column_default, default_type) : null;
|
|
130
|
+
const fieldType = {
|
|
131
|
+
type_name: getFieldType(data_type, character_maximum_length, numeric_precision, numeric_scale),
|
|
132
|
+
schemaName: null,
|
|
133
|
+
};
|
|
134
|
+
return {
|
|
135
|
+
name: column_name,
|
|
136
|
+
type: fieldType,
|
|
137
|
+
dbdefault,
|
|
138
|
+
not_null: !convertQueryBoolean(is_nullable),
|
|
139
|
+
increment: !!identity_increment,
|
|
140
|
+
note: column_comment ? { value: column_comment } : { value: '' },
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
const generateTablesAndFields = async (conn, schemas) => {
|
|
144
|
+
const fields = {};
|
|
145
|
+
const schemaSql = schemas.length > 0 ? `AND c.table_schema IN (${schemas.map((schema) => `'${schema}'`).join(',')})` : '';
|
|
146
|
+
const tablesAndFieldsSql = `
|
|
147
|
+
SELECT
|
|
148
|
+
c.table_schema,
|
|
149
|
+
c.table_name,
|
|
150
|
+
c.column_name,
|
|
151
|
+
c.data_type,
|
|
152
|
+
c.character_maximum_length,
|
|
153
|
+
c.numeric_precision,
|
|
154
|
+
c.numeric_scale,
|
|
155
|
+
c.is_nullable,
|
|
156
|
+
c.column_default,
|
|
157
|
+
CASE
|
|
158
|
+
WHEN c.column_default IS NULL THEN NULL
|
|
159
|
+
WHEN c.column_default LIKE '''%' THEN 'string'
|
|
160
|
+
WHEN LOWER(c.column_default) = 'true' OR LOWER(c.column_default) = 'false' THEN 'boolean'
|
|
161
|
+
WHEN c.column_default REGEXP '^-?[0-9]+(\\.[0-9]+)?$' THEN 'number'
|
|
162
|
+
ELSE 'expression'
|
|
163
|
+
END AS default_type,
|
|
164
|
+
t.comment AS table_comment,
|
|
165
|
+
c.comment AS column_comment,
|
|
166
|
+
c.identity_increment
|
|
167
|
+
FROM
|
|
168
|
+
information_schema.columns c
|
|
169
|
+
JOIN information_schema.tables t
|
|
170
|
+
ON c.table_name = t.table_name
|
|
171
|
+
AND c.table_schema = t.table_schema
|
|
172
|
+
WHERE
|
|
173
|
+
t.table_type = 'BASE TABLE'
|
|
174
|
+
AND t.table_schema NOT IN ('INFORMATION_SCHEMA')
|
|
175
|
+
${schemaSql}
|
|
176
|
+
ORDER BY
|
|
177
|
+
c.table_schema,
|
|
178
|
+
t.created,
|
|
179
|
+
c.ordinal_position;
|
|
180
|
+
`;
|
|
181
|
+
const tablesAndFieldsResult = await executeQuery(conn, tablesAndFieldsSql);
|
|
182
|
+
const tables = tablesAndFieldsResult.reduce((acc, row) => {
|
|
183
|
+
const { TABLE_SCHEMA: table_schema, TABLE_NAME: table_name, TABLE_COMMENT: table_comment } = row;
|
|
184
|
+
const key = `${table_schema}.${table_name}`;
|
|
185
|
+
if (!acc[key]) {
|
|
186
|
+
acc[key] = {
|
|
187
|
+
name: table_name,
|
|
188
|
+
schemaName: table_schema,
|
|
189
|
+
note: table_comment ? { value: table_comment } : { value: '' },
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
if (!fields[key])
|
|
193
|
+
fields[key] = [];
|
|
194
|
+
const field = generateField(row);
|
|
195
|
+
fields[key].push(field);
|
|
196
|
+
return acc;
|
|
197
|
+
}, {});
|
|
198
|
+
return {
|
|
199
|
+
tables: Object.values(tables),
|
|
200
|
+
fields,
|
|
201
|
+
};
|
|
202
|
+
};
|
|
203
|
+
const createConstraintKeysMap = (keys, schemas, constraintType) => {
|
|
204
|
+
return keys.reduce((acc, row) => {
|
|
205
|
+
const { schema_name, table_name, column_name, constraint_name, } = row;
|
|
206
|
+
const selectedSchema = schemas.length > 0 ? schemas.includes(schema_name) : true;
|
|
207
|
+
if (!selectedSchema) {
|
|
208
|
+
return acc;
|
|
209
|
+
}
|
|
210
|
+
const key = `${schema_name}.${table_name}.${constraint_name}`;
|
|
211
|
+
if (acc[key]) {
|
|
212
|
+
const { columnNames } = acc[key];
|
|
213
|
+
columnNames.push(column_name);
|
|
214
|
+
return acc;
|
|
215
|
+
}
|
|
216
|
+
acc[key] = {
|
|
217
|
+
schemaName: schema_name,
|
|
218
|
+
tableName: table_name,
|
|
219
|
+
constraintName: constraint_name,
|
|
220
|
+
columnNames: [column_name],
|
|
221
|
+
type: '',
|
|
222
|
+
};
|
|
223
|
+
acc[key][constraintType] = true;
|
|
224
|
+
return acc;
|
|
225
|
+
}, {});
|
|
226
|
+
};
|
|
227
|
+
const generateIndexes = async (conn, databaseName, schemas) => {
|
|
228
|
+
const getPrimaryKeysSql = `
|
|
229
|
+
SHOW PRIMARY KEYS IN DATABASE ${databaseName};
|
|
230
|
+
`;
|
|
231
|
+
const getUniqueKeysSql = `
|
|
232
|
+
SHOW UNIQUE KEYS IN DATABASE ${databaseName};
|
|
233
|
+
`;
|
|
234
|
+
const primaryKeys = await executeQuery(conn, getPrimaryKeysSql);
|
|
235
|
+
const uniqueKeys = await executeQuery(conn, getUniqueKeysSql);
|
|
236
|
+
const primaryKeysByConstraint = createConstraintKeysMap(primaryKeys, schemas, 'primary');
|
|
237
|
+
const uniqueKeysByConstraint = createConstraintKeysMap(uniqueKeys, schemas, 'unique');
|
|
238
|
+
const allConstraints = [Object.values(primaryKeysByConstraint), Object.values(uniqueKeysByConstraint)].flat();
|
|
239
|
+
const { indexes, tableConstraints } = allConstraints.reduce((acc, row) => {
|
|
240
|
+
const { schemaName, tableName, constraintName, columnNames, type, primary, unique, } = row;
|
|
241
|
+
const key = `${schemaName}.${tableName}`;
|
|
242
|
+
if (columnNames.length < 2) {
|
|
243
|
+
acc.tableConstraints[key] = {
|
|
244
|
+
[columnNames[0]]: {
|
|
245
|
+
pk: primary,
|
|
246
|
+
unique,
|
|
247
|
+
checks: [],
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
return acc;
|
|
251
|
+
}
|
|
252
|
+
const index = {
|
|
253
|
+
name: constraintName,
|
|
254
|
+
type,
|
|
255
|
+
unique,
|
|
256
|
+
pk: primary,
|
|
257
|
+
columns: columnNames.map((columnName) => ({
|
|
258
|
+
type: 'column',
|
|
259
|
+
value: columnName,
|
|
260
|
+
})),
|
|
261
|
+
};
|
|
262
|
+
if (acc.indexes[key]) {
|
|
263
|
+
acc.indexes[key].push(index);
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
acc.indexes[key] = [index];
|
|
267
|
+
}
|
|
268
|
+
return acc;
|
|
269
|
+
}, { indexes: {}, tableConstraints: {} });
|
|
270
|
+
return {
|
|
271
|
+
indexes,
|
|
272
|
+
tableConstraints,
|
|
273
|
+
};
|
|
274
|
+
};
|
|
275
|
+
const fetchSchemaJson = async (connection) => {
|
|
276
|
+
const config = parseConnectionString(connection);
|
|
277
|
+
const conn = await connectToSnowflake(config);
|
|
278
|
+
if (conn instanceof Error) {
|
|
279
|
+
throw conn;
|
|
280
|
+
}
|
|
281
|
+
// Schemas: schema1,schema2,schema3
|
|
282
|
+
const schemas = config.SCHEMAS ? (0, parseSchema_1.parseSchema)(config.SCHEMAS) : [];
|
|
283
|
+
const databaseName = config.DATABASE;
|
|
284
|
+
const tablesAndFieldsRes = generateTablesAndFields(conn, schemas);
|
|
285
|
+
const indexesRes = generateIndexes(conn, databaseName, schemas);
|
|
286
|
+
const res = await Promise.all([
|
|
287
|
+
tablesAndFieldsRes,
|
|
288
|
+
indexesRes,
|
|
289
|
+
]);
|
|
290
|
+
conn.destroy((err) => {
|
|
291
|
+
if (err) {
|
|
292
|
+
throw err;
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
console.log('Connection destroyed successfully.');
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
const { tables, fields } = res[0];
|
|
299
|
+
const { indexes, tableConstraints } = res[1];
|
|
300
|
+
return {
|
|
301
|
+
tables,
|
|
302
|
+
fields,
|
|
303
|
+
refs: [],
|
|
304
|
+
enums: [],
|
|
305
|
+
indexes,
|
|
306
|
+
tableConstraints,
|
|
307
|
+
// Checks are not supported in snowflake
|
|
308
|
+
checks: {},
|
|
309
|
+
};
|
|
310
|
+
};
|
|
311
|
+
exports.fetchSchemaJson = fetchSchemaJson;
|