@quillsql/node 0.5.8 → 0.6.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.
@@ -1,4 +1,3 @@
1
- import { Client } from "../models/Client";
2
1
  import { BigQuery } from "@google-cloud/bigquery";
3
2
  import { QuillQueryResults } from "./DatabaseHelper";
4
3
  import { capitalize, depluralize } from "../utils/textProcessing";
@@ -24,7 +23,7 @@ export function formatBigQueryConfig(connectionString: string): BigQueryConfig {
24
23
  // Validate if required fields are present
25
24
  if (!serviceAccount.project_id || !serviceAccount.private_key) {
26
25
  throw new Error(
27
- "Invalid service account JSON. Required fields are missing."
26
+ "Invalid service account JSON. Required fields are missing.",
28
27
  );
29
28
  }
30
29
 
@@ -44,7 +43,7 @@ export function connectToBigQuery(config: BigQueryConfig) {
44
43
 
45
44
  export async function runQueryBigQuery(
46
45
  sql: string,
47
- bigQuery: BigQuery
46
+ bigQuery: BigQuery,
48
47
  ): Promise<QuillQueryResults> {
49
48
  const rows = await bigQuery.query(sql);
50
49
  if (!rows[0] || rows[0].length === 0) return { fields: [], rows: [] };
@@ -80,7 +79,7 @@ export async function getSchemaBigQuery(bigQuery: BigQuery): Promise<string[]> {
80
79
 
81
80
  export async function getTablesBySchemaBigQuery(
82
81
  bigQuery: BigQuery,
83
- schemaNames: string[]
82
+ schemaNames: string[],
84
83
  ): Promise<{ tableName: string; schemaName: string }[]> {
85
84
  const allColumns = await Promise.all(
86
85
  schemaNames.map(async (schema) => {
@@ -89,7 +88,7 @@ export async function getTablesBySchemaBigQuery(
89
88
  return rows[0].map((row) => {
90
89
  return { tableName: row.table_name, schemaName: schema };
91
90
  });
92
- })
91
+ }),
93
92
  );
94
93
  return allColumns.flat();
95
94
  }
@@ -97,7 +96,7 @@ export async function getTablesBySchemaBigQuery(
97
96
  export async function getColumnsByTableBigQuery(
98
97
  bigQuery: BigQuery,
99
98
  schemaName: string,
100
- tableName: string
99
+ tableName: string,
101
100
  ): Promise<string[]> {
102
101
  const sql = `SELECT column_name FROM ${schemaName}.INFORMATION_SCHEMA.COLUMNS WHERE table_name = '${tableName}'`;
103
102
  const rows = await bigQuery.query(sql);
@@ -108,7 +107,7 @@ export async function getForeignKeysBigQuery(
108
107
  connection: BigQuery,
109
108
  schemaName: string,
110
109
  tableName: string,
111
- primaryKey: string
110
+ primaryKey: string,
112
111
  ): Promise<string[]> {
113
112
  const depluralizedTableName = depluralize(tableName);
114
113
  let sql = `SELECT column_name FROM ${schemaName}.INFORMATION_SCHEMA.COLUMNS
@@ -121,7 +120,7 @@ export async function getForeignKeysBigQuery(
121
120
  return key.column_name;
122
121
  });
123
122
  foreignKeysString = foreignKeysString.filter(
124
- (key) => key !== "id" && key !== "_id_"
123
+ (key) => key !== "id" && key !== "_id_",
125
124
  );
126
125
  foreignKeysString = [...new Set(foreignKeysString)];
127
126
  if (foreignKeysString.length === 0) {
@@ -144,7 +143,7 @@ export async function getForeignKeysBigQuery(
144
143
  export async function getSchemaColumnInfoBigQuery(
145
144
  connection: BigQuery,
146
145
  schemaName: string,
147
- tableNames: { tableName: string; schemaName: string }[]
146
+ tableNames: { tableName: string; schemaName: string }[],
148
147
  ): Promise<
149
148
  { tableName: string; columns: { columnName: string; dataTypeID: number }[] }[]
150
149
  > {
@@ -167,7 +166,7 @@ export async function getSchemaColumnInfoBigQuery(
167
166
  fieldType: row.dataType,
168
167
  })),
169
168
  };
170
- })
169
+ }),
171
170
  );
172
171
  return allColumns;
173
172
  }
@@ -179,6 +178,7 @@ function convertBigQueryTypeToPostgresOID(type: string): number {
179
178
  FLOAT: 700,
180
179
  TIMESTAMP: 1114,
181
180
  DATE: 1082,
181
+ BOOL: 16,
182
182
  };
183
183
 
184
184
  const postgresType = typeToOidMap[type.toUpperCase()] || "VARCHAR"; // Default to 'text' if the type is not recognized
@@ -1,8 +1,7 @@
1
- import { Pool } from "pg";
2
1
  import { Mapable, CacheCredentials } from "../models/Cache";
3
2
  import { QuillConfig } from "../models/Quill";
4
3
  import { createClient } from "redis";
5
- import { isSuperset } from "../utils/Error";
4
+ import { isSuperset, PgError } from "../utils/Error";
6
5
  import {
7
6
  DatabaseConnection,
8
7
  connectToDatabase,
@@ -10,43 +9,35 @@ import {
10
9
  runQueryByDatabase,
11
10
  } from "./DatabaseHelper";
12
11
 
13
- class PgError extends Error {
14
- code?: string;
15
- detail?: string;
16
- hint?: string;
17
- position?: string;
18
- // Add other properties if needed
19
- constructor(detail?: string, hint?: string, position?: string) {
20
- super();
21
- this.detail = detail;
22
- this.hint = hint;
23
- this.position = position;
24
- }
25
- }
26
-
27
12
  /** The TTL for new cache entries (default: 1h) */
28
13
  const DEFAULT_CACHE_TTL = 24 * 60 * 60;
29
14
 
30
15
  export class CachedConnection {
31
16
  public databaseType: "postgresql" | "snowflake" | "bigquery" | "mysql";
32
- public pool: DatabaseConnection;
17
+ public pool: DatabaseConnection | null;
33
18
  public orgId: any;
34
19
  public ttl: number;
35
20
  public cache: Mapable | null;
21
+ private config: any;
22
+ private activeQueries: number = 0;
23
+ private readonly MAX_ACTIVE_QUERIES: number = 0;
36
24
 
37
25
  constructor(
38
26
  databaseType: "postgresql" | "snowflake" | "bigquery" | "mysql",
39
27
  config: any,
40
- cacheConfig: Partial<CacheCredentials> = {}
28
+ cacheConfig: Partial<CacheCredentials> = {},
41
29
  ) {
42
30
  this.databaseType = databaseType;
43
31
  this.pool = connectToDatabase(databaseType, config);
32
+ this.config = config;
44
33
  this.ttl = cacheConfig?.ttl ?? DEFAULT_CACHE_TTL;
45
34
  this.cache = this.getCache(cacheConfig);
46
35
  }
47
36
 
48
- public async query(text: string, values?: any[]): Promise<any> {
37
+ public async query(text: string): Promise<any> {
49
38
  try {
39
+ this.activeQueries++;
40
+ this.pool = this.getPool();
50
41
  if (!this.cache) {
51
42
  return await runQueryByDatabase(this.databaseType, this.pool, text);
52
43
  }
@@ -58,7 +49,7 @@ export class CachedConnection {
58
49
  const newResult = await runQueryByDatabase(
59
50
  this.databaseType,
60
51
  this.pool,
61
- text
52
+ text,
62
53
  );
63
54
  const newResultString: string = JSON.stringify(newResult);
64
55
  await this.cache.set(key, newResultString, "EX", DEFAULT_CACHE_TTL);
@@ -67,13 +58,21 @@ export class CachedConnection {
67
58
  } catch (err) {
68
59
  if (isSuperset(err, PgError)) {
69
60
  throw new PgError(
61
+ (err as any).message,
70
62
  (err as any).detail,
71
63
  (err as any).hint,
72
- (err as any).position
64
+ (err as any).position,
73
65
  );
74
66
  } else if (err instanceof Error) {
75
67
  throw new Error(err.message);
76
68
  }
69
+ } finally {
70
+ this.activeQueries--;
71
+ if (this.activeQueries <= this.MAX_ACTIVE_QUERIES) {
72
+ if (this.databaseType.toLowerCase() === "mysql") {
73
+ this.close();
74
+ }
75
+ }
77
76
  }
78
77
  }
79
78
 
@@ -89,14 +88,24 @@ export class CachedConnection {
89
88
  }: QuillConfig["cache"]): Mapable | null {
90
89
  if (cacheType === "redis" || cacheType === "rediss") {
91
90
  const redisURL = `${cacheType}://${username}:${password}@${host}:${port}`;
92
- const client = createClient({ url: redisURL });
91
+ const client = createClient({ url: redisURL });
93
92
  client.connect();
94
93
  return client as Mapable;
95
94
  }
96
95
  return null;
97
96
  }
98
97
 
98
+ public getPool() {
99
+ if (!this.pool) {
100
+ this.pool = connectToDatabase(this.databaseType, this.config);
101
+ }
102
+ return this.pool;
103
+ }
104
+
99
105
  async close() {
100
- await disconnectFromDatabase(this.databaseType, this.pool);
106
+ if (this.pool) {
107
+ disconnectFromDatabase(this.databaseType, this.pool);
108
+ this.pool = null;
109
+ }
101
110
  }
102
111
  }
@@ -1,6 +1,5 @@
1
1
  import { Pool } from "pg";
2
2
  import snowflake from "snowflake-sdk";
3
- import { Client } from "../models/Client";
4
3
  import { Pool as MysqlPool } from "mysql2";
5
4
  import { BigQuery } from "@google-cloud/bigquery";
6
5
  import {
@@ -65,7 +64,7 @@ export interface QuillQueryResults {
65
64
 
66
65
  export function getDatabaseCredentials(
67
66
  databaseType: "postgresql" | "snowflake" | "bigquery" | "mysql",
68
- connectionString: string
67
+ connectionString: string,
69
68
  ):
70
69
  | PostgresConnectionConfig
71
70
  | SnowflakeConnectionConfig
@@ -94,7 +93,7 @@ export function connectToDatabase(
94
93
  | PostgresConnectionConfig
95
94
  | SnowflakeConnectionConfig
96
95
  | BigQueryConfig
97
- | MysqlConnectionConfig
96
+ | MysqlConnectionConfig,
98
97
  ): DatabaseConnection {
99
98
  switch (databaseType.toLowerCase()) {
100
99
  case "postgres":
@@ -115,7 +114,7 @@ export function connectToDatabase(
115
114
  export function runQueryByDatabase(
116
115
  databaseType: "postgresql" | "snowflake" | "bigquery" | "mysql",
117
116
  connection: DatabaseConnection,
118
- sql: string
117
+ sql: string,
119
118
  ): Promise<QuillQueryResults> | undefined {
120
119
  switch (databaseType.toLowerCase()) {
121
120
  case "postgres":
@@ -135,7 +134,7 @@ export function runQueryByDatabase(
135
134
 
136
135
  export function disconnectFromDatabase(
137
136
  databaseType: "postgresql" | "snowflake" | "bigquery" | "mysql",
138
- database: DatabaseConnection
137
+ database: DatabaseConnection,
139
138
  ) {
140
139
  switch (databaseType.toLowerCase()) {
141
140
  case "postgres":
@@ -155,7 +154,7 @@ export function disconnectFromDatabase(
155
154
 
156
155
  export async function getSchemasByDatabase(
157
156
  databaseType: "postgresql" | "snowflake" | "bigquery" | "mysql",
158
- connection: DatabaseConnection
157
+ connection: DatabaseConnection,
159
158
  ): Promise<string[] | undefined> {
160
159
  switch (databaseType.toLowerCase()) {
161
160
  case "postgres":
@@ -177,33 +176,33 @@ export async function getSchemasByDatabase(
177
176
  export async function getTablesBySchemaByDatabase(
178
177
  databaseType: "postgresql" | "snowflake" | "bigquery" | "mysql",
179
178
  connection: DatabaseConnection,
180
- schemaName: string | string[]
179
+ schemaName: string | string[],
181
180
  ): Promise<string[] | { tableName: string; schemaName: string }[] | undefined> {
182
181
  switch (databaseType.toLowerCase()) {
183
182
  case "postgres":
184
183
  return getTablesBySchemaPostgres(
185
184
  connection as Pool,
186
- schemaName as string[]
185
+ schemaName as string[],
187
186
  );
188
187
  case "postgresql":
189
188
  return getTablesBySchemaPostgres(
190
189
  connection as Pool,
191
- schemaName as string[]
190
+ schemaName as string[],
192
191
  );
193
192
  case "snowflake":
194
193
  return getTablesBySchemaSnowflake(
195
194
  connection as snowflake.Connection,
196
- schemaName as string[]
195
+ schemaName as string[],
197
196
  );
198
197
  case "bigquery":
199
198
  return getTablesBySchemaBigQuery(
200
199
  connection as BigQuery,
201
- schemaName as string[]
200
+ schemaName as string[],
202
201
  );
203
202
  case "mysql":
204
203
  return getTablesBySchemaMysql(
205
204
  connection as MysqlPool,
206
- schemaName as string[]
205
+ schemaName as string[],
207
206
  );
208
207
  default:
209
208
  return undefined;
@@ -215,38 +214,38 @@ export async function getColumnsByTableByDatabase(
215
214
  databaseType: "postgresql" | "snowflake" | "bigquery" | "mysql",
216
215
  connection: DatabaseConnection,
217
216
  schemaName: string,
218
- tableName: string
217
+ tableName: string,
219
218
  ): Promise<string[] | undefined> {
220
219
  switch (databaseType.toLowerCase()) {
221
220
  case "postgres":
222
221
  return getColumnsByTablePostgres(
223
222
  connection as Pool,
224
223
  schemaName,
225
- tableName
224
+ tableName,
226
225
  );
227
226
  case "postgresql":
228
227
  return getColumnsByTablePostgres(
229
228
  connection as Pool,
230
229
  schemaName,
231
- tableName
230
+ tableName,
232
231
  );
233
232
  case "snowflake":
234
233
  return getColumnsByTableSnowflake(
235
234
  connection as snowflake.Connection,
236
235
  schemaName,
237
- tableName
236
+ tableName,
238
237
  );
239
238
  case "bigquery":
240
239
  return getColumnsByTableBigQuery(
241
240
  connection as BigQuery,
242
241
  schemaName,
243
- tableName
242
+ tableName,
244
243
  );
245
244
  case "mysql":
246
245
  return getColumnsByTableMysql(
247
246
  connection as MysqlPool,
248
247
  schemaName,
249
- tableName
248
+ tableName,
250
249
  );
251
250
  default:
252
251
  return undefined;
@@ -258,7 +257,7 @@ export async function getForiegnKeysByDatabase(
258
257
  connection: DatabaseConnection,
259
258
  schemaName: string,
260
259
  tableName: string,
261
- primaryKey: string
260
+ primaryKey: string,
262
261
  ): Promise<string[] | undefined> {
263
262
  switch (databaseType.toLowerCase()) {
264
263
  case "postgres":
@@ -266,35 +265,35 @@ export async function getForiegnKeysByDatabase(
266
265
  connection as Pool,
267
266
  schemaName,
268
267
  tableName,
269
- primaryKey
268
+ primaryKey,
270
269
  );
271
270
  case "postgresql":
272
271
  return getForeignKeysPostgres(
273
272
  connection as Pool,
274
273
  schemaName,
275
274
  tableName,
276
- primaryKey
275
+ primaryKey,
277
276
  );
278
277
  case "snowflake":
279
278
  return getForeignKeysSnowflake(
280
279
  connection as snowflake.Connection,
281
280
  schemaName,
282
281
  tableName,
283
- primaryKey
282
+ primaryKey,
284
283
  );
285
284
  case "bigquery":
286
285
  return getForeignKeysBigQuery(
287
286
  connection as BigQuery,
288
287
  schemaName,
289
288
  tableName,
290
- primaryKey
289
+ primaryKey,
291
290
  );
292
291
  case "mysql":
293
292
  return getForeignKeysMysql(
294
293
  connection as MysqlPool,
295
294
  schemaName,
296
295
  tableName,
297
- primaryKey
296
+ primaryKey,
298
297
  );
299
298
  default:
300
299
  return undefined;
@@ -305,38 +304,38 @@ export function getColumnInfoBySchemaByDatabase(
305
304
  databaseType: "postgresql" | "snowflake" | "bigquery" | "mysql",
306
305
  connection: DatabaseConnection,
307
306
  schemaName: string,
308
- tables: string[] | { tableName: string; schemaName: string }[]
307
+ tables: string[] | { tableName: string; schemaName: string }[],
309
308
  ) {
310
309
  switch (databaseType.toLowerCase()) {
311
310
  case "postgres":
312
311
  return getSchemaColumnInfoPostgress(
313
312
  connection as Pool,
314
313
  schemaName,
315
- tables as { tableName: string; schemaName: string }[]
314
+ tables as { tableName: string; schemaName: string }[],
316
315
  );
317
316
  case "postgresql":
318
317
  return getSchemaColumnInfoPostgress(
319
318
  connection as Pool,
320
319
  schemaName,
321
- tables as { tableName: string; schemaName: string }[]
320
+ tables as { tableName: string; schemaName: string }[],
322
321
  );
323
322
  case "snowflake":
324
323
  return getSchemaColumnInfoSnowflake(
325
324
  connection as snowflake.Connection,
326
325
  schemaName,
327
- tables as { tableName: string; schemaName: string }[]
326
+ tables as { tableName: string; schemaName: string }[],
328
327
  );
329
328
  case "bigquery":
330
329
  return getSchemaColumnInfoBigQuery(
331
330
  connection as BigQuery,
332
331
  schemaName,
333
- tables as { tableName: string; schemaName: string }[]
332
+ tables as { tableName: string; schemaName: string }[],
334
333
  );
335
334
  case "mysql":
336
335
  return getSchemaColumnInfoMysql(
337
336
  connection as MysqlPool,
338
337
  schemaName,
339
- tables as { tableName: string; schemaName: string }[]
338
+ tables as { tableName: string; schemaName: string }[],
340
339
  );
341
340
  default:
342
341
  return undefined;
package/src/db/Mysql.ts CHANGED
@@ -1,5 +1,3 @@
1
- import { connect } from "http2";
2
- import { Client } from "../models/Client";
3
1
  import mysql, { Pool as MysqlPool } from "mysql2";
4
2
  import { QuillQueryResults } from "./DatabaseHelper";
5
3
  import url from "url";
@@ -14,7 +12,7 @@ export interface MysqlConnectionConfig {
14
12
  }
15
13
 
16
14
  export function formatMysqlConfig(
17
- connectionString: string
15
+ connectionString: string,
18
16
  ): MysqlConnectionConfig {
19
17
  const parsedUrl = url.parse(connectionString);
20
18
  const [user, password] = (parsedUrl.auth || "").split(":");
@@ -31,7 +29,7 @@ export function connectToMysql(config: MysqlConnectionConfig): MysqlPool {
31
29
  const pool = mysql.createPool({
32
30
  ...config,
33
31
  waitForConnections: true,
34
- connectionLimit: 10,
32
+ connectionLimit: 128,
35
33
  queueLimit: 0,
36
34
  });
37
35
  return pool;
@@ -43,7 +41,7 @@ export function disconnectFromMysql(connection: MysqlPool) {
43
41
 
44
42
  export async function runQueryMysql(
45
43
  sql: string,
46
- connection: MysqlPool
44
+ connection: MysqlPool,
47
45
  ): Promise<QuillQueryResults> {
48
46
  const result: QuillQueryResults = await new Promise((resolve, reject) => {
49
47
  connection.query(sql, (error, results, fields) => {
@@ -72,7 +70,7 @@ export async function runQueryMysql(
72
70
  }
73
71
 
74
72
  export async function getSchemasMysql(
75
- connection: MysqlPool
73
+ connection: MysqlPool,
76
74
  ): Promise<string[]> {
77
75
  const sql = `SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA
78
76
  WHERE schema_name != 'information_schema'
@@ -84,7 +82,7 @@ export async function getSchemasMysql(
84
82
 
85
83
  export async function getTablesBySchemaMysql(
86
84
  connection: MysqlPool,
87
- schemaNames: string[]
85
+ schemaNames: string[],
88
86
  ): Promise<{ tableName: string; schemaName: string }[]> {
89
87
  const allColumns = await Promise.all(
90
88
  schemaNames.map(async (schema) => {
@@ -93,7 +91,7 @@ export async function getTablesBySchemaMysql(
93
91
  return results.rows.map((row) => {
94
92
  return { tableName: row.TABLE_NAME, schemaName: row.TABLE_SCHEMA };
95
93
  });
96
- })
94
+ }),
97
95
  );
98
96
  return allColumns.flat();
99
97
  }
@@ -101,7 +99,7 @@ export async function getTablesBySchemaMysql(
101
99
  export async function getColumnsByTableMysql(
102
100
  connection: MysqlPool,
103
101
  schemaName: string,
104
- tableName: string
102
+ tableName: string,
105
103
  ): Promise<string[]> {
106
104
  const sql = `SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${schemaName}' AND TABLE_NAME = '${tableName}'`;
107
105
  const results = await runQueryMysql(sql, connection);
@@ -112,7 +110,7 @@ export async function getForeignKeysMysql(
112
110
  connection: MysqlPool,
113
111
  schemaName: string,
114
112
  tableName: string,
115
- primaryKey: string
113
+ primaryKey: string,
116
114
  ): Promise<string[]> {
117
115
  const depluralizedTableName = depluralize(tableName);
118
116
  let sql = `SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
@@ -127,7 +125,7 @@ export async function getForeignKeysMysql(
127
125
  });
128
126
  // remove any foreignKeyStrings that are just 'id'
129
127
  foreignKeysString = foreignKeysString.filter(
130
- (key) => key !== "id" && key !== "_id_"
128
+ (key) => key !== "id" && key !== "_id_",
131
129
  );
132
130
  foreignKeysString = [...new Set(foreignKeysString)];
133
131
  if (foreignKeysString.length === 0) {
@@ -151,7 +149,7 @@ export async function getForeignKeysMysql(
151
149
  export async function getSchemaColumnInfoMysql(
152
150
  connection: MysqlPool,
153
151
  schemaName: string,
154
- tableNames: { tableName: string; schemaName: string }[]
152
+ tableNames: { tableName: string; schemaName: string }[],
155
153
  ): Promise<
156
154
  { tableName: string; columns: { columnName: string; dataTypeID: number }[] }[]
157
155
  > {
@@ -174,7 +172,7 @@ export async function getSchemaColumnInfoMysql(
174
172
  fieldType: row.dataType,
175
173
  })),
176
174
  };
177
- })
175
+ }),
178
176
  );
179
177
  return allColumns;
180
178
  }
@@ -191,6 +189,10 @@ function mysqlTextDataTypeToPostgresOID(type: string): number {
191
189
  return 1043;
192
190
  case "timestamp": // date
193
191
  return 1082;
192
+ case "date": // date
193
+ return 1082;
194
+ case "datetime": // date
195
+ return 1082;
194
196
  default: // varchar
195
197
  return 1043;
196
198
  }
@@ -210,7 +212,9 @@ function mysqlDataTypeIdToPostgresType(type: number): number {
210
212
  return 1043;
211
213
  case 7: // date
212
214
  return 1082;
213
- case 7: // date
215
+ case 10: // date
216
+ return 1082;
217
+ case 12: // date
214
218
  return 1082;
215
219
  default: // varchar
216
220
  return 1043;