@malloydata/db-postgres 0.0.195-dev241003204905 → 0.0.195-dev241007154000
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/postgres.spec.js +20 -25
- package/dist/postgres_connection.d.ts +3 -16
- package/dist/postgres_connection.js +20 -101
- package/package.json +2 -2
package/dist/postgres.spec.js
CHANGED
|
@@ -45,34 +45,23 @@ describe('PostgresConnection', () => {
|
|
|
45
45
|
beforeEach(async () => {
|
|
46
46
|
getTableSchema = jest
|
|
47
47
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
48
|
-
.spyOn(postgres_connection_1.PostgresConnection.prototype, '
|
|
48
|
+
.spyOn(postgres_connection_1.PostgresConnection.prototype, 'fetchTableSchema')
|
|
49
49
|
.mockResolvedValue({
|
|
50
|
-
type: '
|
|
50
|
+
type: 'table',
|
|
51
51
|
dialect: 'postgres',
|
|
52
52
|
name: 'name',
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
type: 'basetable',
|
|
56
|
-
connectionName: 'postgres',
|
|
57
|
-
},
|
|
58
|
-
fields: [],
|
|
53
|
+
tablePath: 'test',
|
|
54
|
+
connection: 'postgres',
|
|
59
55
|
});
|
|
60
56
|
getSQLBlockSchema = jest
|
|
61
57
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
62
|
-
.spyOn(postgres_connection_1.PostgresConnection.prototype, '
|
|
58
|
+
.spyOn(postgres_connection_1.PostgresConnection.prototype, 'fetchSelectSchema')
|
|
63
59
|
.mockResolvedValue({
|
|
64
|
-
type: '
|
|
60
|
+
type: 'sql select',
|
|
65
61
|
dialect: 'postgres',
|
|
66
62
|
name: 'name',
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
method: 'subquery',
|
|
70
|
-
sqlBlock: SQL_BLOCK_1,
|
|
71
|
-
},
|
|
72
|
-
structRelationship: {
|
|
73
|
-
type: 'basetable',
|
|
74
|
-
connectionName: 'postgres',
|
|
75
|
-
},
|
|
63
|
+
selectStr: SQL_BLOCK_1.selectStr,
|
|
64
|
+
connection: 'postgres',
|
|
76
65
|
fields: [],
|
|
77
66
|
});
|
|
78
67
|
});
|
|
@@ -92,23 +81,26 @@ describe('PostgresConnection', () => {
|
|
|
92
81
|
expect(getTableSchema).toBeCalledTimes(2);
|
|
93
82
|
});
|
|
94
83
|
it('caches sql schema', async () => {
|
|
95
|
-
await connection.
|
|
84
|
+
await connection.fetchSchemaForSQLStruct(SQL_BLOCK_1, {});
|
|
96
85
|
expect(getSQLBlockSchema).toBeCalledTimes(1);
|
|
97
|
-
await connection.
|
|
86
|
+
await connection.fetchSchemaForSQLStruct(SQL_BLOCK_1, {});
|
|
98
87
|
expect(getSQLBlockSchema).toBeCalledTimes(1);
|
|
99
88
|
});
|
|
100
89
|
it('refreshes sql schema', async () => {
|
|
101
|
-
await connection.
|
|
90
|
+
await connection.fetchSchemaForSQLStruct(SQL_BLOCK_2, {});
|
|
102
91
|
expect(getSQLBlockSchema).toBeCalledTimes(1);
|
|
103
|
-
await connection.
|
|
92
|
+
await connection.fetchSchemaForSQLStruct(SQL_BLOCK_2, {
|
|
104
93
|
refreshTimestamp: Date.now() + 10,
|
|
105
94
|
});
|
|
106
95
|
expect(getSQLBlockSchema).toBeCalledTimes(2);
|
|
107
96
|
});
|
|
108
97
|
});
|
|
109
98
|
const SQL_BLOCK_1 = {
|
|
110
|
-
type: '
|
|
99
|
+
type: 'sql_select',
|
|
111
100
|
name: 'block1',
|
|
101
|
+
dialect: 'postgres',
|
|
102
|
+
connection: 'postgres',
|
|
103
|
+
fields: [],
|
|
112
104
|
selectStr: `
|
|
113
105
|
SELECT
|
|
114
106
|
created_at,
|
|
@@ -124,8 +116,11 @@ FROM "inventory_items.parquet"
|
|
|
124
116
|
`,
|
|
125
117
|
};
|
|
126
118
|
const SQL_BLOCK_2 = {
|
|
127
|
-
type: '
|
|
119
|
+
type: 'sql_select',
|
|
128
120
|
name: 'block2',
|
|
121
|
+
dialect: 'postgres',
|
|
122
|
+
connection: 'postgres',
|
|
123
|
+
fields: [],
|
|
129
124
|
selectStr: `
|
|
130
125
|
SELECT
|
|
131
126
|
created_at,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Connection, ConnectionConfig,
|
|
1
|
+
import { Connection, ConnectionConfig, MalloyQueryData, PersistSQLResults, PooledConnection, QueryDataRow, QueryOptionsReader, QueryRunStats, RunSQLOptions, SQLSourceDef, TableSourceDef, StreamingConnection } from '@malloydata/malloy';
|
|
2
2
|
import { BaseConnection } from '@malloydata/malloy/connection';
|
|
3
3
|
import { Client, Pool } from 'pg';
|
|
4
4
|
interface PostgresConnectionConfiguration {
|
|
@@ -17,8 +17,6 @@ export declare class PostgresConnection extends BaseConnection implements Connec
|
|
|
17
17
|
private queryOptionsReader;
|
|
18
18
|
private configReader;
|
|
19
19
|
private readonly dialect;
|
|
20
|
-
private schemaCache;
|
|
21
|
-
private sqlSchemaCache;
|
|
22
20
|
constructor(options: PostgresConnectionOptions, queryOptionsReader?: QueryOptionsReader);
|
|
23
21
|
constructor(name: string, queryOptionsReader?: QueryOptionsReader, configReader?: PostgresConnectionConfigurationReader);
|
|
24
22
|
private readQueryConfig;
|
|
@@ -28,22 +26,11 @@ export declare class PostgresConnection extends BaseConnection implements Connec
|
|
|
28
26
|
canPersist(): this is PersistSQLResults;
|
|
29
27
|
canStream(): this is StreamingConnection;
|
|
30
28
|
get supportsNesting(): boolean;
|
|
31
|
-
fetchSchemaForTables(missing: Record<string, string>, { refreshTimestamp }: FetchSchemaOptions): Promise<{
|
|
32
|
-
schemas: Record<string, StructDef>;
|
|
33
|
-
errors: Record<string, string>;
|
|
34
|
-
}>;
|
|
35
|
-
fetchSchemaForSQLBlock(sqlRef: SQLBlock, { refreshTimestamp }: FetchSchemaOptions): Promise<{
|
|
36
|
-
structDef: StructDef;
|
|
37
|
-
error?: undefined;
|
|
38
|
-
} | {
|
|
39
|
-
error: string;
|
|
40
|
-
structDef?: undefined;
|
|
41
|
-
}>;
|
|
42
29
|
protected getClient(): Promise<Client>;
|
|
43
30
|
protected runPostgresQuery(sqlCommand: string, _pageSize: number, _rowIndex: number, deJSON: boolean): Promise<MalloyQueryData>;
|
|
44
|
-
|
|
31
|
+
fetchSelectSchema(sqlRef: SQLSourceDef): Promise<SQLSourceDef | string>;
|
|
45
32
|
private schemaFromQuery;
|
|
46
|
-
|
|
33
|
+
fetchTableSchema(tableKey: string, tablePath: string): Promise<TableSourceDef | string>;
|
|
47
34
|
test(): Promise<void>;
|
|
48
35
|
connectionSetup(client: Client): Promise<void>;
|
|
49
36
|
runSQL(sql: string, { rowLimit }?: RunSQLOptions, rowIndex?: number): Promise<MalloyQueryData>;
|
|
@@ -68,8 +68,6 @@ class PostgresConnection extends connection_1.BaseConnection {
|
|
|
68
68
|
this.queryOptionsReader = {};
|
|
69
69
|
this.configReader = {};
|
|
70
70
|
this.dialect = new malloy_1.PostgresDialect();
|
|
71
|
-
this.schemaCache = new Map();
|
|
72
|
-
this.sqlSchemaCache = new Map();
|
|
73
71
|
if (typeof arg === 'string') {
|
|
74
72
|
this.name = arg;
|
|
75
73
|
if (configReader) {
|
|
@@ -116,54 +114,6 @@ class PostgresConnection extends connection_1.BaseConnection {
|
|
|
116
114
|
get supportsNesting() {
|
|
117
115
|
return true;
|
|
118
116
|
}
|
|
119
|
-
async fetchSchemaForTables(missing, { refreshTimestamp }) {
|
|
120
|
-
const schemas = {};
|
|
121
|
-
const errors = {};
|
|
122
|
-
for (const tableKey in missing) {
|
|
123
|
-
let inCache = this.schemaCache.get(tableKey);
|
|
124
|
-
if (!inCache ||
|
|
125
|
-
(refreshTimestamp && refreshTimestamp > inCache.timestamp)) {
|
|
126
|
-
const tablePath = missing[tableKey];
|
|
127
|
-
const timestamp = refreshTimestamp || Date.now();
|
|
128
|
-
try {
|
|
129
|
-
inCache = {
|
|
130
|
-
schema: await this.getTableSchema(tableKey, tablePath),
|
|
131
|
-
timestamp,
|
|
132
|
-
};
|
|
133
|
-
this.schemaCache.set(tableKey, inCache);
|
|
134
|
-
}
|
|
135
|
-
catch (error) {
|
|
136
|
-
inCache = { error: error.message, timestamp };
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (inCache.schema !== undefined) {
|
|
140
|
-
schemas[tableKey] = inCache.schema;
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
errors[tableKey] = inCache.error || 'Unknown schema fetch error';
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return { schemas, errors };
|
|
147
|
-
}
|
|
148
|
-
async fetchSchemaForSQLBlock(sqlRef, { refreshTimestamp }) {
|
|
149
|
-
const key = sqlRef.name;
|
|
150
|
-
let inCache = this.sqlSchemaCache.get(key);
|
|
151
|
-
if (!inCache ||
|
|
152
|
-
(refreshTimestamp && refreshTimestamp > inCache.timestamp)) {
|
|
153
|
-
const timestamp = refreshTimestamp !== null && refreshTimestamp !== void 0 ? refreshTimestamp : Date.now();
|
|
154
|
-
try {
|
|
155
|
-
inCache = {
|
|
156
|
-
structDef: await this.getSQLBlockSchema(sqlRef),
|
|
157
|
-
timestamp,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
catch (error) {
|
|
161
|
-
inCache = { error: error.message, timestamp };
|
|
162
|
-
}
|
|
163
|
-
this.sqlSchemaCache.set(key, inCache);
|
|
164
|
-
}
|
|
165
|
-
return inCache;
|
|
166
|
-
}
|
|
167
117
|
async getClient() {
|
|
168
118
|
const { username: user, password, databaseName: database, port, host, connectionString, } = await this.readConfig();
|
|
169
119
|
return new pg_1.Client({
|
|
@@ -194,22 +144,8 @@ class PostgresConnection extends connection_1.BaseConnection {
|
|
|
194
144
|
totalRows: result.rows.length,
|
|
195
145
|
};
|
|
196
146
|
}
|
|
197
|
-
async
|
|
198
|
-
const structDef = {
|
|
199
|
-
type: 'struct',
|
|
200
|
-
dialect: 'postgres',
|
|
201
|
-
name: sqlRef.name,
|
|
202
|
-
structSource: {
|
|
203
|
-
type: 'sql',
|
|
204
|
-
method: 'subquery',
|
|
205
|
-
sqlBlock: sqlRef,
|
|
206
|
-
},
|
|
207
|
-
structRelationship: {
|
|
208
|
-
type: 'basetable',
|
|
209
|
-
connectionName: this.name,
|
|
210
|
-
},
|
|
211
|
-
fields: [],
|
|
212
|
-
};
|
|
147
|
+
async fetchSelectSchema(sqlRef) {
|
|
148
|
+
const structDef = { ...sqlRef, fields: [] };
|
|
213
149
|
const tempTableName = `tmp${(0, crypto_1.randomUUID)()}`.replace(/-/g, '');
|
|
214
150
|
const infoQuery = `
|
|
215
151
|
drop table if exists ${tempTableName};
|
|
@@ -226,7 +162,7 @@ class PostgresConnection extends connection_1.BaseConnection {
|
|
|
226
162
|
await this.schemaFromQuery(infoQuery, structDef);
|
|
227
163
|
}
|
|
228
164
|
catch (error) {
|
|
229
|
-
|
|
165
|
+
return `Error fetching schema for ${sqlRef.name}: ${error}`;
|
|
230
166
|
}
|
|
231
167
|
return structDef;
|
|
232
168
|
}
|
|
@@ -237,53 +173,36 @@ class PostgresConnection extends connection_1.BaseConnection {
|
|
|
237
173
|
}
|
|
238
174
|
for (const row of rows) {
|
|
239
175
|
const postgresDataType = row['data_type'];
|
|
240
|
-
|
|
241
|
-
let malloyType = this.dialect.sqlTypeToMalloyType(postgresDataType);
|
|
242
|
-
let name = row['column_name'];
|
|
176
|
+
const name = row['column_name'];
|
|
243
177
|
if (postgresDataType === 'ARRAY') {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
type: '
|
|
247
|
-
|
|
178
|
+
const elementType = this.dialect.sqlTypeToMalloyType(row['element_type']);
|
|
179
|
+
structDef.fields.push({
|
|
180
|
+
type: 'array',
|
|
181
|
+
elementTypeDef: elementType,
|
|
182
|
+
name,
|
|
248
183
|
dialect: this.dialectName,
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
isArray: true,
|
|
253
|
-
},
|
|
254
|
-
structSource: { type: 'nested' },
|
|
255
|
-
fields: [],
|
|
256
|
-
};
|
|
257
|
-
structDef.fields.push(s);
|
|
258
|
-
name = 'value';
|
|
259
|
-
}
|
|
260
|
-
if (malloyType) {
|
|
261
|
-
s.fields.push({ ...malloyType, name });
|
|
184
|
+
join: 'many',
|
|
185
|
+
fields: (0, malloy_1.arrayEachFields)(elementType),
|
|
186
|
+
});
|
|
262
187
|
}
|
|
263
188
|
else {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
rawType: postgresDataType.toLowerCase(),
|
|
267
|
-
name,
|
|
268
|
-
});
|
|
189
|
+
const malloyType = this.dialect.sqlTypeToMalloyType(postgresDataType);
|
|
190
|
+
structDef.fields.push({ ...malloyType, name });
|
|
269
191
|
}
|
|
270
192
|
}
|
|
271
193
|
}
|
|
272
|
-
async
|
|
194
|
+
async fetchTableSchema(tableKey, tablePath) {
|
|
273
195
|
const structDef = {
|
|
274
|
-
type: '
|
|
196
|
+
type: 'table',
|
|
275
197
|
name: tableKey,
|
|
276
198
|
dialect: 'postgres',
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
type: 'basetable',
|
|
280
|
-
connectionName: this.name,
|
|
281
|
-
},
|
|
199
|
+
tablePath,
|
|
200
|
+
connection: this.name,
|
|
282
201
|
fields: [],
|
|
283
202
|
};
|
|
284
203
|
const [schema, table] = tablePath.split('.');
|
|
285
204
|
if (table === undefined) {
|
|
286
|
-
|
|
205
|
+
return 'Default schema not yet supported in Postgres';
|
|
287
206
|
}
|
|
288
207
|
const infoQuery = `
|
|
289
208
|
SELECT column_name, c.data_type, e.data_type as element_type
|
|
@@ -297,7 +216,7 @@ class PostgresConnection extends connection_1.BaseConnection {
|
|
|
297
216
|
await this.schemaFromQuery(infoQuery, structDef);
|
|
298
217
|
}
|
|
299
218
|
catch (error) {
|
|
300
|
-
|
|
219
|
+
return `Error fetching schema for ${tablePath}: ${error.message}`;
|
|
301
220
|
}
|
|
302
221
|
return structDef;
|
|
303
222
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@malloydata/db-postgres",
|
|
3
|
-
"version": "0.0.195-
|
|
3
|
+
"version": "0.0.195-dev241007154000",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"prepublishOnly": "npm run build"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@malloydata/malloy": "^0.0.195-
|
|
25
|
+
"@malloydata/malloy": "^0.0.195-dev241007154000",
|
|
26
26
|
"@types/pg": "^8.6.1",
|
|
27
27
|
"pg": "^8.7.1",
|
|
28
28
|
"pg-query-stream": "4.2.3"
|