@malloydata/db-postgres 0.0.295 → 0.0.296

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.
@@ -69,7 +69,6 @@ const malloy_1 = require("@malloydata/malloy");
69
69
  const connection_1 = require("@malloydata/malloy/connection");
70
70
  const pg_1 = require("pg");
71
71
  const pg_query_stream_1 = __importDefault(require("pg-query-stream"));
72
- const crypto_1 = require("crypto");
73
72
  const DEFAULT_PAGE_SIZE = 1000;
74
73
  const SCHEMA_PAGE_SIZE = 1000;
75
74
  class PostgresConnection extends connection_1.BaseConnection {
@@ -162,24 +161,94 @@ class PostgresConnection extends connection_1.BaseConnection {
162
161
  fields: [],
163
162
  name: (0, malloy_1.sqlKey)(sqlRef.connection, sqlRef.selectStr),
164
163
  };
165
- const tempTableName = `tmp${(0, crypto_1.randomUUID)()}`.replace(/-/g, '');
166
- const infoQuery = `
167
- drop table if exists ${tempTableName};
168
- create temp table ${tempTableName} as SELECT * FROM (
169
- ${sqlRef.selectStr}
170
- ) as x where false;
171
- SELECT column_name, c.data_type, e.data_type as element_type
172
- FROM information_schema.columns c LEFT JOIN information_schema.element_types e
173
- ON ((c.table_catalog, c.table_schema, c.table_name, 'TABLE', c.dtd_identifier)
174
- = (e.object_catalog, e.object_schema, e.object_name, e.object_type, e.collection_type_identifier))
175
- where table_name='${tempTableName}';
176
- `;
177
- try {
178
- await this.schemaFromQuery(infoQuery, structDef);
164
+ const client = await this.getClient();
165
+ await client.connect();
166
+ await this.connectionSetup(client);
167
+ // 1) Get row-descriptor without fetching data
168
+ const res = await client.query({
169
+ text: `SELECT * FROM (${sqlRef.selectStr}) _t LIMIT 0`,
170
+ });
171
+ // 2) Resolve every OID we might touch (field, array element, domain base)
172
+ const neededOids = new Set();
173
+ res.fields.forEach(f => neededOids.add(f.dataTypeID));
174
+ // we'll add more OIDs later (typelem / typebasetype) lazily
175
+ // helper to fetch pg_type rows on demand, with cache
176
+ const pgTypeCache = new Map();
177
+ const loadTypes = async (oids) => {
178
+ if (oids.length === 0)
179
+ return;
180
+ const params = oids.map((_, i) => `$${i + 1}`).join(',');
181
+ const { rows } = await client.query(`
182
+ SELECT
183
+ oid,
184
+ typname,
185
+ typtype,
186
+ typcategory,
187
+ typelem,
188
+ typbasetype,
189
+ format_type(oid, NULL) AS formatted
190
+ FROM pg_type
191
+ WHERE oid IN (${params})
192
+ `, oids);
193
+ rows.forEach(r => pgTypeCache.set(r.oid, r));
194
+ };
195
+ // Prime the cache
196
+ await loadTypes([...neededOids]);
197
+ // 3) recursive mapper → info-schema compliant strings
198
+ const mapDataType = async (oid) => {
199
+ let t = pgTypeCache.get(oid);
200
+ if (!t) {
201
+ await loadTypes([oid]);
202
+ t = pgTypeCache.get(oid);
203
+ }
204
+ // ARRAY?
205
+ if (t.typcategory === 'A')
206
+ return 'ARRAY';
207
+ // DOMAIN? recurse to its base type
208
+ if (t.typtype === 'd')
209
+ return mapDataType(t.typbasetype);
210
+ // ENUM, COMPOSITE, RANGE, MULTIRANGE, PSEUDO → USER-DEFINED
211
+ if (['e', 'c', 'r', 'm', 'p'].includes(t.typtype))
212
+ return 'USER-DEFINED';
213
+ // built-in scalar or base type of domain
214
+ return t.formatted;
215
+ };
216
+ // helper to resolve element_type (NULL for scalars)
217
+ const mapElementType = async (oid) => {
218
+ let t = pgTypeCache.get(oid);
219
+ if (!t) {
220
+ await loadTypes([oid]);
221
+ t = pgTypeCache.get(oid);
222
+ }
223
+ if (t.typcategory !== 'A')
224
+ return null; // not an array
225
+ // Ensure element row cached
226
+ if (!pgTypeCache.has(t.typelem))
227
+ await loadTypes([t.typelem]);
228
+ return mapDataType(t.typelem);
229
+ };
230
+ // 4) Build final array in original column order
231
+ const result = [];
232
+ for (const field of res.fields) {
233
+ result.push({
234
+ columnName: field.name,
235
+ dataType: await mapDataType(field.dataTypeID),
236
+ elementType: await mapElementType(field.dataTypeID),
237
+ });
179
238
  }
180
- catch (error) {
181
- return `Error fetching schema for ${structDef.name}: ${error}`;
239
+ for (const row of result) {
240
+ const postgresDataType = row.dataType;
241
+ const name = row.columnName;
242
+ if (postgresDataType === 'ARRAY') {
243
+ const elementType = this.dialect.sqlTypeToMalloyType(row.elementType);
244
+ structDef.fields.push((0, malloy_1.mkArrayDef)(elementType, name));
245
+ }
246
+ else {
247
+ const malloyType = this.dialect.sqlTypeToMalloyType(postgresDataType);
248
+ structDef.fields.push({ ...malloyType, name });
249
+ }
182
250
  }
251
+ await client.end();
183
252
  return structDef;
184
253
  }
185
254
  async schemaFromQuery(infoQuery, structDef) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/db-postgres",
3
- "version": "0.0.295",
3
+ "version": "0.0.296",
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.295",
25
+ "@malloydata/malloy": "0.0.296",
26
26
  "@types/pg": "^8.6.1",
27
27
  "pg": "^8.7.1",
28
28
  "pg-query-stream": "4.2.3"