@quillsql/node 0.3.7 → 0.3.8
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/assets/pgtypes.js +2785 -0
- package/dist/db/BigQuery.js +189 -0
- package/dist/db/{CachedPools.js → CachedConnection.js} +11 -23
- package/dist/db/DatabaseHelper.js +187 -0
- package/dist/db/Mysql.js +187 -0
- package/dist/db/Postgres.js +156 -0
- package/dist/db/Snowflake.js +179 -0
- package/dist/index.js +47 -18
- package/dist/index.uspec.js +3 -2
- package/dist/models/Client.js +2 -0
- package/dist/utils/RunQueryProcesses.js +4 -4
- package/dist/utils/textProcessing.js +17 -0
- package/examples/node-server/app.ts +6 -8
- package/package.json +5 -1
- package/src/assets/pgtypes.ts +2782 -0
- package/src/db/BigQuery.ts +201 -0
- package/src/db/{CachedPools.ts → CachedConnection.ts} +25 -21
- package/src/db/DatabaseHelper.ts +340 -0
- package/src/db/Mysql.ts +209 -0
- package/src/db/Postgres.ts +178 -0
- package/src/db/Snowflake.ts +191 -0
- package/src/index.ts +69 -18
- package/src/index.uspec.ts +9 -2
- package/src/models/Client.ts +29 -0
- package/src/models/Quill.ts +0 -6
- package/src/utils/RunQueryProcesses.ts +5 -5
- package/src/utils/textProcessing.ts +13 -0
package/src/db/Mysql.ts
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { connect } from "http2";
|
|
2
|
+
import { Client } from "../models/Client";
|
|
3
|
+
import mysql, { Pool as MysqlPool } from "mysql2";
|
|
4
|
+
import { QuillQueryResults } from "./DatabaseHelper";
|
|
5
|
+
import url from "url";
|
|
6
|
+
import { capitalize, depluralize } from "../utils/textProcessing";
|
|
7
|
+
|
|
8
|
+
export interface MysqlConnectionConfig {
|
|
9
|
+
host: string;
|
|
10
|
+
user: string;
|
|
11
|
+
password: string;
|
|
12
|
+
database: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function formatMysqlConfig(
|
|
16
|
+
connectionString: string
|
|
17
|
+
): MysqlConnectionConfig {
|
|
18
|
+
const parsedUrl = url.parse(connectionString);
|
|
19
|
+
const [user, password] = (parsedUrl.auth || "").split(":");
|
|
20
|
+
return {
|
|
21
|
+
host: parsedUrl.hostname || "",
|
|
22
|
+
user: user || "",
|
|
23
|
+
password: password || "",
|
|
24
|
+
database: (parsedUrl.pathname || "").slice(1),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function connectToMysql(config: MysqlConnectionConfig): MysqlPool {
|
|
29
|
+
const pool = mysql.createPool({
|
|
30
|
+
...config,
|
|
31
|
+
waitForConnections: true,
|
|
32
|
+
connectionLimit: 10,
|
|
33
|
+
queueLimit: 0,
|
|
34
|
+
});
|
|
35
|
+
return pool;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function disconnectFromMysql(connection: MysqlPool) {
|
|
39
|
+
connection.end();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function runQueryMysql(
|
|
43
|
+
sql: string,
|
|
44
|
+
connection: MysqlPool
|
|
45
|
+
): Promise<QuillQueryResults> {
|
|
46
|
+
const result: QuillQueryResults = await new Promise((resolve, reject) => {
|
|
47
|
+
connection.query(sql, (error, results, fields) => {
|
|
48
|
+
if (error) {
|
|
49
|
+
reject(error);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const mappedFields = fields
|
|
54
|
+
? fields.map((field) => ({
|
|
55
|
+
name: field.name,
|
|
56
|
+
dataTypeID: mysqlDataTypeIdToPostgresType(field.type || 1043), // Provide a default value for dataTypeID
|
|
57
|
+
}))
|
|
58
|
+
: [];
|
|
59
|
+
|
|
60
|
+
const processRows = Array.isArray(results)
|
|
61
|
+
? results.map((row: any) => {
|
|
62
|
+
return JSON.parse(JSON.stringify(row));
|
|
63
|
+
})
|
|
64
|
+
: [];
|
|
65
|
+
|
|
66
|
+
resolve({ fields: mappedFields, rows: processRows });
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function getSchemasMysql(
|
|
73
|
+
connection: MysqlPool
|
|
74
|
+
): Promise<string[]> {
|
|
75
|
+
const sql = `SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA
|
|
76
|
+
WHERE schema_name != 'information_schema'
|
|
77
|
+
AND schema_name != 'performance_schema'
|
|
78
|
+
and schema_name != 'sys';`;
|
|
79
|
+
const results = await runQueryMysql(sql, connection);
|
|
80
|
+
return results.rows.map((row) => row.SCHEMA_NAME);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function getTablesBySchemaMysql(
|
|
84
|
+
connection: MysqlPool,
|
|
85
|
+
schemaName: string
|
|
86
|
+
): Promise<string[]> {
|
|
87
|
+
const sql = `SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '${schemaName}'`;
|
|
88
|
+
const results = await runQueryMysql(sql, connection);
|
|
89
|
+
return results.rows.map((row) => row.TABLE_NAME);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function getColumnsByTableMysql(
|
|
93
|
+
connection: MysqlPool,
|
|
94
|
+
schemaName: string,
|
|
95
|
+
tableName: string
|
|
96
|
+
): Promise<string[]> {
|
|
97
|
+
const sql = `SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${schemaName}' AND TABLE_NAME = '${tableName}'`;
|
|
98
|
+
const results = await runQueryMysql(sql, connection);
|
|
99
|
+
return results.rows.map((row) => row.COLUMN_NAME);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function getForeignKeysMysql(
|
|
103
|
+
connection: MysqlPool,
|
|
104
|
+
schemaName: string,
|
|
105
|
+
tableName: string,
|
|
106
|
+
primaryKey: string
|
|
107
|
+
): Promise<string[]> {
|
|
108
|
+
const depluralizedTableName = depluralize(tableName);
|
|
109
|
+
let sql = `SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
|
|
110
|
+
WHERE TABLE_SCHEMA = '${schemaName}'
|
|
111
|
+
and table_name != '${tableName}'
|
|
112
|
+
and (column_name = '${primaryKey}'
|
|
113
|
+
or column_name = '${depluralizedTableName}\\_${primaryKey}'
|
|
114
|
+
or column_name = '${depluralizedTableName}${capitalize(primaryKey)}' )`;
|
|
115
|
+
const results = await runQueryMysql(sql, connection);
|
|
116
|
+
let foreignKeysString = results.rows.map((key) => {
|
|
117
|
+
return key.COLUMN_NAME;
|
|
118
|
+
});
|
|
119
|
+
// remove any foreignKeyStrings that are just 'id'
|
|
120
|
+
foreignKeysString = foreignKeysString.filter(
|
|
121
|
+
(key) => key !== "id" && key !== "_id_"
|
|
122
|
+
);
|
|
123
|
+
foreignKeysString = [...new Set(foreignKeysString)];
|
|
124
|
+
if (foreignKeysString.length === 0) {
|
|
125
|
+
sql = `SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
|
|
126
|
+
WHERE TABLE_SCHEMA = '${schemaName}'
|
|
127
|
+
and table_name != '${tableName}'
|
|
128
|
+
and (column_name like '${depluralizedTableName}%'
|
|
129
|
+
or column_name like '%\\_id'
|
|
130
|
+
or column_name like '%Id'
|
|
131
|
+
or column_name like '%\\_${primaryKey}'
|
|
132
|
+
or column_name like '%${capitalize(primaryKey)}')`;
|
|
133
|
+
const results = await runQueryMysql(sql, connection);
|
|
134
|
+
foreignKeysString = results.rows.map((key) => {
|
|
135
|
+
return key.COLUMN_NAME;
|
|
136
|
+
});
|
|
137
|
+
foreignKeysString = [...new Set(foreignKeysString)];
|
|
138
|
+
}
|
|
139
|
+
return foreignKeysString;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function getSchemaColumnInfoMysql(
|
|
143
|
+
connection: MysqlPool,
|
|
144
|
+
schemaName: string,
|
|
145
|
+
tableNames: string[]
|
|
146
|
+
): Promise<
|
|
147
|
+
{ tableName: string; columns: { columnName: string; dataTypeId: number }[] }[]
|
|
148
|
+
> {
|
|
149
|
+
const allColumns = await Promise.all(
|
|
150
|
+
tableNames.map(async (tableName) => {
|
|
151
|
+
const query = `
|
|
152
|
+
SELECT COLUMN_NAME as columnName,
|
|
153
|
+
DATA_TYPE as dataType FROM INFORMATION_SCHEMA.COLUMNS
|
|
154
|
+
WHERE TABLE_SCHEMA = '${schemaName}'
|
|
155
|
+
AND TABLE_NAME = '${tableName}'
|
|
156
|
+
`;
|
|
157
|
+
const results = await runQueryMysql(query, connection);
|
|
158
|
+
return {
|
|
159
|
+
tableName,
|
|
160
|
+
displayName: tableName,
|
|
161
|
+
columns: results.rows.map((row: any) => ({
|
|
162
|
+
columnName: row.columnName,
|
|
163
|
+
displayName: row.columnName,
|
|
164
|
+
dataTypeId: mysqlTextDataTypeToPostgresOID(row.dataType),
|
|
165
|
+
fieldType: row.dataType,
|
|
166
|
+
})),
|
|
167
|
+
};
|
|
168
|
+
})
|
|
169
|
+
);
|
|
170
|
+
return allColumns;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function mysqlTextDataTypeToPostgresOID(type: string): number {
|
|
174
|
+
switch (type) {
|
|
175
|
+
case "bigint": // int
|
|
176
|
+
return 23;
|
|
177
|
+
case "tinyint": // int
|
|
178
|
+
return 23;
|
|
179
|
+
case "float": // float
|
|
180
|
+
return 700;
|
|
181
|
+
case "varchar": // varchar
|
|
182
|
+
return 1043;
|
|
183
|
+
case "timestamp": // date
|
|
184
|
+
return 1082;
|
|
185
|
+
default: // varchar
|
|
186
|
+
return 1043;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function mysqlDataTypeIdToPostgresType(type: number): number {
|
|
191
|
+
switch (type) {
|
|
192
|
+
case 8: // int
|
|
193
|
+
return 23;
|
|
194
|
+
case 3: // int
|
|
195
|
+
return 23;
|
|
196
|
+
case 2: // int
|
|
197
|
+
return 23;
|
|
198
|
+
case 5: // float
|
|
199
|
+
return 700;
|
|
200
|
+
case 253: // varchar
|
|
201
|
+
return 1043;
|
|
202
|
+
case 7: // date
|
|
203
|
+
return 1082;
|
|
204
|
+
case 7: // date
|
|
205
|
+
return 1082;
|
|
206
|
+
default: // varchar
|
|
207
|
+
return 1043;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { Pool } from "pg";
|
|
2
|
+
import { Client } from "../models/Client";
|
|
3
|
+
import { disconnect } from "process";
|
|
4
|
+
import { QuillQueryResults } from "./DatabaseHelper";
|
|
5
|
+
import { capitalize, depluralize } from "../utils/textProcessing";
|
|
6
|
+
import { PG_TYPES } from "../assets/pgtypes";
|
|
7
|
+
import { run } from "node:test";
|
|
8
|
+
|
|
9
|
+
export type PostgresConnectionConfig = {
|
|
10
|
+
connectionString: string;
|
|
11
|
+
ssl?: {
|
|
12
|
+
rejectUnauthorized: false;
|
|
13
|
+
ca?: string;
|
|
14
|
+
key?: string;
|
|
15
|
+
cert?: string;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function connectToPostgres(config: PostgresConnectionConfig): Pool {
|
|
20
|
+
return new Pool(config);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function disconnectFromPostgres(pool: Pool) {
|
|
24
|
+
pool.end();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function runQueryPostgres(
|
|
28
|
+
sql: string,
|
|
29
|
+
pool: Pool
|
|
30
|
+
): Promise<QuillQueryResults> {
|
|
31
|
+
const results = await pool.query(sql);
|
|
32
|
+
return {
|
|
33
|
+
fields: results.fields.map((field: any) => ({
|
|
34
|
+
name: field.name,
|
|
35
|
+
dataTypeID: field.dataTypeID,
|
|
36
|
+
})),
|
|
37
|
+
rows: results.rows,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function getSchemasPostgres(pool: Pool): Promise<string[]> {
|
|
42
|
+
const sql = `SELECT schema_name FROM information_schema.schemata
|
|
43
|
+
WHERE schema_name NOT LIKE 'pg_%' AND schema_name != 'information_schema';`;
|
|
44
|
+
const results = await runQueryPostgres(sql, pool);
|
|
45
|
+
return results.rows.map((row) => row.schema_name);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function getTablesBySchemaPostgres(
|
|
49
|
+
pool: Pool,
|
|
50
|
+
schemaName: string
|
|
51
|
+
): Promise<string[]> {
|
|
52
|
+
const sql = `SELECT table_name FROM information_schema.tables WHERE table_schema = '${schemaName}'`;
|
|
53
|
+
const results = await runQueryPostgres(sql, pool);
|
|
54
|
+
return results.rows.map((row) => row.table_name);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function getColumnsByTablePostgres(
|
|
58
|
+
pool: Pool,
|
|
59
|
+
schemaName: string,
|
|
60
|
+
tableName: string
|
|
61
|
+
): Promise<string[]> {
|
|
62
|
+
const sql = `SELECT column_name FROM information_schema.columns WHERE table_schema = '${schemaName}' and table_name = '${tableName}'`;
|
|
63
|
+
const results = await runQueryPostgres(sql, pool);
|
|
64
|
+
return results.rows.map((row) => row.column_name);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function getForeignKeysPostgres(
|
|
68
|
+
pool: Pool,
|
|
69
|
+
schemaName: string,
|
|
70
|
+
tableName: string,
|
|
71
|
+
primaryKey: string
|
|
72
|
+
): Promise<string[]> {
|
|
73
|
+
const depluralizedTableName = depluralize(tableName);
|
|
74
|
+
let sql = `SELECT column_name FROM information_schema.columns
|
|
75
|
+
WHERE table_schema = '${schemaName}'
|
|
76
|
+
and table_name != '${tableName}'
|
|
77
|
+
and (column_name = '${primaryKey}'
|
|
78
|
+
or column_name = '${depluralizedTableName}_${primaryKey}'
|
|
79
|
+
or column_name = '${depluralizedTableName}${capitalize(primaryKey)}')`;
|
|
80
|
+
const results = await runQueryPostgres(sql, pool);
|
|
81
|
+
let foreignKeysString = results.rows.map((key) => {
|
|
82
|
+
return key.column_name;
|
|
83
|
+
});
|
|
84
|
+
foreignKeysString = foreignKeysString.filter((key) => key !== "id" && key !== "_id_");
|
|
85
|
+
foreignKeysString = [...new Set(foreignKeysString)];
|
|
86
|
+
if (foreignKeysString.length === 0) {
|
|
87
|
+
sql = `SELECT column_name FROM information_schema.columns
|
|
88
|
+
WHERE table_schema = '${schemaName}'
|
|
89
|
+
and table_name != '${tableName}'
|
|
90
|
+
and (column_name like '${tableName}%'
|
|
91
|
+
or column_name like '%\\_id'
|
|
92
|
+
or column_name like '%Id'
|
|
93
|
+
or column_name like '%\\_${primaryKey}'
|
|
94
|
+
or column_name like '%${capitalize(primaryKey)}')`;
|
|
95
|
+
const results = await runQueryPostgres(sql, pool);
|
|
96
|
+
foreignKeysString = results.rows.map((key) => {
|
|
97
|
+
return key.column_name;
|
|
98
|
+
});
|
|
99
|
+
foreignKeysString = [...new Set(foreignKeysString)];
|
|
100
|
+
}
|
|
101
|
+
return foreignKeysString;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function getSchemaColumnInfoPostgress(
|
|
105
|
+
pool: Pool,
|
|
106
|
+
schemaName: string,
|
|
107
|
+
tableNames: string[]
|
|
108
|
+
): Promise<
|
|
109
|
+
{ tableName: string; columns: { columnName: string; dataTypeId: number }[] }[]
|
|
110
|
+
> {
|
|
111
|
+
const allColumns = await Promise.all(
|
|
112
|
+
tableNames.map(async (tableName) => {
|
|
113
|
+
const query = `
|
|
114
|
+
SELECT column_name as "columnName", udt_name as "fieldType"
|
|
115
|
+
FROM information_schema.columns
|
|
116
|
+
WHERE table_schema = '${schemaName}'
|
|
117
|
+
AND table_name = '${tableName}'
|
|
118
|
+
ORDER BY ordinal_position;
|
|
119
|
+
`;
|
|
120
|
+
const results = await runQueryPostgres(query, pool);
|
|
121
|
+
return {
|
|
122
|
+
tableName,
|
|
123
|
+
displayName: tableName,
|
|
124
|
+
columns: results.rows.map((row) => {
|
|
125
|
+
let pgType = PG_TYPES.find((pgType) => {
|
|
126
|
+
return pgType.typname === row.fieldType;
|
|
127
|
+
})?.oid;
|
|
128
|
+
if (!pgType) {
|
|
129
|
+
pgType = 1043;
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
columnName: row.columnName,
|
|
133
|
+
displayName: row.columnName,
|
|
134
|
+
dataTypeId: pgType,
|
|
135
|
+
fieldType: row.fieldType,
|
|
136
|
+
};
|
|
137
|
+
}),
|
|
138
|
+
};
|
|
139
|
+
})
|
|
140
|
+
);
|
|
141
|
+
return allColumns;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function formatPostgresConfig(
|
|
145
|
+
connectionString: string
|
|
146
|
+
): PostgresConnectionConfig {
|
|
147
|
+
return { connectionString, ssl: { rejectUnauthorized: false } };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// CURRENTLY UNUSED BUT MAYBE USEFUL IN THE FUTURE
|
|
151
|
+
function getSslConfig(client: Client):
|
|
152
|
+
| {
|
|
153
|
+
rejectUnauthorized: false;
|
|
154
|
+
ca?: string;
|
|
155
|
+
key?: string;
|
|
156
|
+
cert?: string;
|
|
157
|
+
}
|
|
158
|
+
| undefined {
|
|
159
|
+
if (!client.useSsl) {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
if (client.serverCa && client.clientKey && client.clientCert) {
|
|
163
|
+
return {
|
|
164
|
+
rejectUnauthorized: false,
|
|
165
|
+
ca: client.serverCa,
|
|
166
|
+
key: client.clientKey,
|
|
167
|
+
cert: client.clientCert,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
if (client.serverCa) {
|
|
171
|
+
return {
|
|
172
|
+
rejectUnauthorized: false,
|
|
173
|
+
ca: client.serverCa,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
// if using ssl with no certificates
|
|
177
|
+
return { rejectUnauthorized: false };
|
|
178
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import snowflake from "snowflake-sdk";
|
|
2
|
+
import { Client } from "../models/Client";
|
|
3
|
+
import { QuillQueryResults } from "./DatabaseHelper";
|
|
4
|
+
import { capitalize, depluralize } from "../utils/textProcessing";
|
|
5
|
+
|
|
6
|
+
const POSTGRES_SNOWFLAKE_MAP: { [type: string]: number } = {
|
|
7
|
+
BOOLEAN: 16,
|
|
8
|
+
FIXED: 1700, // DECIMAL or NUMERIC
|
|
9
|
+
REAL: 700, // FLOAT4
|
|
10
|
+
DOUBLE: 701, // FLOAT8
|
|
11
|
+
TEXT: 25, // TEXT in PostgreSQL
|
|
12
|
+
DATE: 1082,
|
|
13
|
+
DATETIME: 1184, // TIMESTAMP in PostgreSQL
|
|
14
|
+
TIME: 1083,
|
|
15
|
+
TIMESTAMP_LTZ: 1184,
|
|
16
|
+
TIMESTAMP_NTZ: 1184,
|
|
17
|
+
TIMESTAMP_TZ: 1184,
|
|
18
|
+
VARIANT: 114, // JSONB in PostgreSQL
|
|
19
|
+
OBJECT: 114, // JSONB, as an equivalent for structured JSON
|
|
20
|
+
ARRAY: 1009, // TEXT[], assuming most common case
|
|
21
|
+
BINARY: 17, // BYTEA
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type SnowflakeConnectionConfig = {
|
|
25
|
+
account: string;
|
|
26
|
+
username: string;
|
|
27
|
+
password: string;
|
|
28
|
+
database: string;
|
|
29
|
+
warehouse: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export async function runQuerySnowflake(
|
|
33
|
+
sql: string,
|
|
34
|
+
connection: snowflake.Connection
|
|
35
|
+
): Promise<QuillQueryResults> {
|
|
36
|
+
const results = await new Promise((resolve, reject) => {
|
|
37
|
+
connection.execute({
|
|
38
|
+
sqlText: sql,
|
|
39
|
+
complete: (err, stmt, rows) => {
|
|
40
|
+
if (err) {
|
|
41
|
+
reject(err);
|
|
42
|
+
return { success: false, message: err.message };
|
|
43
|
+
} else {
|
|
44
|
+
resolve({
|
|
45
|
+
rows,
|
|
46
|
+
fields: stmt.getColumns().map((col) => ({
|
|
47
|
+
name: col.getName(),
|
|
48
|
+
dataTypeID: POSTGRES_SNOWFLAKE_MAP[col.getType().toUpperCase()],
|
|
49
|
+
})),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
return results as QuillQueryResults;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function getSchemasSnowflake(
|
|
59
|
+
connection: snowflake.Connection
|
|
60
|
+
): Promise<string[]> {
|
|
61
|
+
const sql = `SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA
|
|
62
|
+
WHERE SCHEMA_NAME != 'INFORMATION_SCHEMA'`;
|
|
63
|
+
const results = await runQuerySnowflake(sql, connection);
|
|
64
|
+
return results.rows.map((row) => row.SCHEMA_NAME);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function getTablesBySchemaSnowflake(
|
|
68
|
+
connection: snowflake.Connection,
|
|
69
|
+
schema: string
|
|
70
|
+
): Promise<string[]> {
|
|
71
|
+
const sql = `SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '${schema}'`;
|
|
72
|
+
const results = await runQuerySnowflake(sql, connection);
|
|
73
|
+
return results.rows.map((row) => row.TABLE_NAME);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function getColumnsByTableSnowflake(
|
|
77
|
+
connection: snowflake.Connection,
|
|
78
|
+
schemaName: string,
|
|
79
|
+
tableName: string
|
|
80
|
+
): Promise<string[]> {
|
|
81
|
+
const sql = `SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${schemaName}' AND TABLE_NAME = '${tableName}'`;
|
|
82
|
+
const results = await runQuerySnowflake(sql, connection);
|
|
83
|
+
return results.rows.map((row) => row.COLUMN_NAME);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function formatSnowflakeConfig(
|
|
87
|
+
connectionString: string
|
|
88
|
+
): SnowflakeConnectionConfig {
|
|
89
|
+
const parsed = new URL(connectionString);
|
|
90
|
+
return {
|
|
91
|
+
account: parsed.hostname,
|
|
92
|
+
username: parsed.username,
|
|
93
|
+
password: parsed.password,
|
|
94
|
+
database: parsed.pathname.split("/")[1],
|
|
95
|
+
warehouse: parsed.pathname.split("/")[2],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function connectToSnowflake(
|
|
100
|
+
config: SnowflakeConnectionConfig
|
|
101
|
+
): snowflake.Connection {
|
|
102
|
+
const connection = snowflake.createConnection(config);
|
|
103
|
+
connection.connect((err) => {
|
|
104
|
+
if (err) {
|
|
105
|
+
console.error(`Failed to connect to Snowflake: ${err.message}`);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
return connection;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function disconnectFromSnowflake(
|
|
112
|
+
connection: snowflake.Connection
|
|
113
|
+
) {
|
|
114
|
+
connection.destroy((err, conn) => {
|
|
115
|
+
if (err) {
|
|
116
|
+
console.error(`Failed to disconnect from Snowflake: ${err.message}`);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export async function getForeignKeysSnowflake(
|
|
122
|
+
connection: snowflake.Connection,
|
|
123
|
+
schemaName: string,
|
|
124
|
+
tableName: string,
|
|
125
|
+
primaryKey: string
|
|
126
|
+
): Promise<string[]> {
|
|
127
|
+
let depluralizedTableName = depluralize(tableName);
|
|
128
|
+
let sql = `SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
|
|
129
|
+
WHERE TABLE_SCHEMA = '${schemaName}'
|
|
130
|
+
AND TABLE_NAME = '${schemaName}'
|
|
131
|
+
and TABLE_NAME != '${tableName}'
|
|
132
|
+
and (COLUMN_NAME = '${primaryKey}'
|
|
133
|
+
or COLUMN_NAME = '${depluralizedTableName}_${primaryKey}'
|
|
134
|
+
or COLUMN_NAME = '${depluralizedTableName}${capitalize(primaryKey)}')`;
|
|
135
|
+
const results = await runQuerySnowflake(sql, connection);
|
|
136
|
+
let foreignKeysString = results.rows.map((key) => {
|
|
137
|
+
return key.COLUMN_NAME;
|
|
138
|
+
});
|
|
139
|
+
foreignKeysString = foreignKeysString.filter(
|
|
140
|
+
(key) => key !== "id" && key !== "_id_"
|
|
141
|
+
);
|
|
142
|
+
foreignKeysString = [...new Set(foreignKeysString)];
|
|
143
|
+
if (foreignKeysString.length === 0) {
|
|
144
|
+
sql = `SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
|
|
145
|
+
WHERE TABLE_SCHEMA = '${schemaName}'
|
|
146
|
+
AND TABLE_NAME = '${schemaName}'
|
|
147
|
+
and TABLE_NAME != '${tableName}'
|
|
148
|
+
and (COLUMN_NAME like '${depluralizedTableName}%'
|
|
149
|
+
or column_name like '%\\_id'
|
|
150
|
+
or column_name like '%Id'
|
|
151
|
+
or COLUMN_NAME like '%\\_${primaryKey}'
|
|
152
|
+
or COLUMN_NAME like '%${capitalize(primaryKey)}')`;
|
|
153
|
+
const results = await runQuerySnowflake(sql, connection);
|
|
154
|
+
foreignKeysString = results.rows.map((key) => {
|
|
155
|
+
return key.COLUMN_NAME;
|
|
156
|
+
});
|
|
157
|
+
foreignKeysString = [...new Set(foreignKeysString)];
|
|
158
|
+
}
|
|
159
|
+
return foreignKeysString;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export async function getSchemaColumnInfoSnowflake(
|
|
163
|
+
connection: snowflake.Connection,
|
|
164
|
+
schemaName: string,
|
|
165
|
+
tableNames: string[]
|
|
166
|
+
) {
|
|
167
|
+
const allColumns = await Promise.all(
|
|
168
|
+
tableNames.map(async (tableName) => {
|
|
169
|
+
const query = `SELECT
|
|
170
|
+
COLUMN_NAME as "columnName", DATA_TYPE as "dataType"
|
|
171
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
172
|
+
WHERE TABLE_SCHEMA = '${schemaName}' AND TABLE_NAME = '${tableName}';
|
|
173
|
+
`;
|
|
174
|
+
const results = await runQuerySnowflake(query, connection);
|
|
175
|
+
return {
|
|
176
|
+
tableName,
|
|
177
|
+
displayName: tableName,
|
|
178
|
+
columns: results.rows.map((row) => {
|
|
179
|
+
const postgresType = POSTGRES_SNOWFLAKE_MAP[row.dataType];
|
|
180
|
+
return {
|
|
181
|
+
columnName: row.columnName,
|
|
182
|
+
displayName: row.columnName,
|
|
183
|
+
dataTypeId: postgresType,
|
|
184
|
+
fieldType: row.dataType,
|
|
185
|
+
};
|
|
186
|
+
}),
|
|
187
|
+
};
|
|
188
|
+
})
|
|
189
|
+
);
|
|
190
|
+
return allColumns;
|
|
191
|
+
}
|