@malloy-publisher/server 0.0.132 → 0.0.134

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/server.js CHANGED
@@ -131914,6 +131914,8 @@ function validateAndBuildTrinoConfig(trinoConfig) {
131914
131914
  }
131915
131915
  };
131916
131916
  delete baseConfig.password;
131917
+ delete baseConfig.catalog;
131918
+ delete baseConfig.schema;
131917
131919
  } else if (trinoConfig.server?.startsWith("https://") && trinoConfig.password) {
131918
131920
  baseConfig.password = trinoConfig.password;
131919
131921
  }
@@ -132541,7 +132543,35 @@ async function getSchemasForConnection(connection, malloyConnection) {
132541
132543
  throw new Error("Trino connection is required");
132542
132544
  }
132543
132545
  try {
132544
- const result = await malloyConnection.runSQL(`SHOW SCHEMAS FROM ${connection.trinoConnection.catalog}`);
132546
+ let result;
132547
+ if (connection.trinoConnection.catalog) {
132548
+ result = await malloyConnection.runSQL(`SHOW SCHEMAS FROM ${connection.trinoConnection.catalog}`);
132549
+ } else {
132550
+ const catalogs = await malloyConnection.runSQL(`SHOW CATALOGS`);
132551
+ console.log("catalogs", catalogs);
132552
+ let catalogNames = standardizeRunSQLResult(catalogs);
132553
+ catalogNames = catalogNames.map((catalog) => {
132554
+ const typedCatalog = catalog;
132555
+ return typedCatalog.Catalog;
132556
+ });
132557
+ const schemas = [];
132558
+ console.log("catalogNames", catalogNames);
132559
+ for (const catalog of catalogNames) {
132560
+ const schemasResult = await malloyConnection.runSQL(`SHOW SCHEMAS FROM ${catalog}`);
132561
+ const schemasResultRows = standardizeRunSQLResult(schemasResult);
132562
+ console.log("schemasResultRows", schemasResultRows);
132563
+ const schemasWithCatalog = schemasResultRows.map((row) => {
132564
+ const typedRow = row;
132565
+ return {
132566
+ ...typedRow,
132567
+ Schema: `${catalog}.${typedRow.Schema ?? typedRow.schema ?? ""}`
132568
+ };
132569
+ });
132570
+ schemas.push(...schemasWithCatalog);
132571
+ console.log("schemas", schemas);
132572
+ }
132573
+ result = schemas;
132574
+ }
132545
132575
  const rows = standardizeRunSQLResult(result);
132546
132576
  return rows.map((row) => {
132547
132577
  const typedRow = row;
@@ -132618,7 +132648,11 @@ async function getTablesForSchema(connection, schemaName, malloyConnection) {
132618
132648
  try {
132619
132649
  let tablePath;
132620
132650
  if (connection.type === "trino") {
132621
- tablePath = `${connection.trinoConnection?.catalog}.${schemaName}.${tableName}`;
132651
+ if (connection.trinoConnection?.catalog) {
132652
+ tablePath = `${connection.trinoConnection?.catalog}.${schemaName}.${tableName}`;
132653
+ } else {
132654
+ tablePath = `${schemaName}.${tableName}`;
132655
+ }
132622
132656
  } else {
132623
132657
  tablePath = `${schemaName}.${tableName}`;
132624
132658
  }
@@ -132651,15 +132685,20 @@ async function getConnectionTableSource(malloyConnection, tableKey, tablePath) {
132651
132685
  });
132652
132686
  const source = await malloyConnection.fetchTableSchema(tableKey, tablePath);
132653
132687
  if (source === undefined) {
132654
- throw new ConnectionError(`Table ${tablePath} not found`);
132688
+ throw new ConnectionError(`Table ${tablePath} not found: ${JSON.stringify(source)}`);
132655
132689
  }
132656
- if (!source || typeof source !== "object") {
132690
+ if (!source) {
132657
132691
  throw new ConnectionError(`Invalid table source returned for ${tablePath}`);
132692
+ } else if (typeof source !== "object") {
132693
+ throw new ConnectionError(JSON.stringify(source));
132658
132694
  }
132659
132695
  const malloyFields = source.fields;
132660
132696
  if (!malloyFields || !Array.isArray(malloyFields)) {
132661
132697
  throw new ConnectionError(`Table ${tablePath} has no fields or invalid field structure`);
132662
132698
  }
132699
+ if (malloyFields.length === 0) {
132700
+ throw new ConnectionError(`Table ${tablePath} not found`);
132701
+ }
132663
132702
  const fields = malloyFields.map((field) => {
132664
132703
  return {
132665
132704
  name: field.name,
@@ -132675,8 +132714,13 @@ async function getConnectionTableSource(malloyConnection, tableKey, tablePath) {
132675
132714
  columns: fields
132676
132715
  };
132677
132716
  } catch (error) {
132678
- logger2.error("fetchTableSchema error", { error, tableKey, tablePath });
132679
- throw new ConnectionError(error.message);
132717
+ const errorMessage = error instanceof Error ? error.message : typeof error === "string" ? error : JSON.stringify(error);
132718
+ logger2.error("fetchTableSchema error", {
132719
+ error,
132720
+ tableKey,
132721
+ tablePath
132722
+ });
132723
+ throw new ConnectionError(errorMessage);
132680
132724
  }
132681
132725
  }
132682
132726
  async function listTablesForSchema(connection, schemaName, malloyConnection) {
@@ -132737,7 +132781,12 @@ async function listTablesForSchema(connection, schemaName, malloyConnection) {
132737
132781
  throw new Error("Trino connection is required");
132738
132782
  }
132739
132783
  try {
132740
- const result = await malloyConnection.runSQL(`SHOW TABLES FROM ${connection.trinoConnection.catalog}.${schemaName}`);
132784
+ let result;
132785
+ if (connection.trinoConnection?.catalog) {
132786
+ result = await malloyConnection.runSQL(`SHOW TABLES FROM ${connection.trinoConnection.catalog}.${schemaName}`);
132787
+ } else {
132788
+ result = await malloyConnection.runSQL(`SHOW TABLES FROM ${schemaName}`);
132789
+ }
132741
132790
  const rows = standardizeRunSQLResult(result);
132742
132791
  return rows.map((row) => {
132743
132792
  const typedRow = row;
@@ -139063,7 +139112,34 @@ run: ${sourceName ? sourceName + "->" : ""}${queryName}`);
139063
139112
  const rowLimit = (await runnable.getPreparedResult()).resultExplore.limit || ROW_LIMIT;
139064
139113
  const endTime = performance.now();
139065
139114
  const executionTime = endTime - startTime;
139066
- const queryResults = await runnable.run({ rowLimit });
139115
+ let queryResults;
139116
+ try {
139117
+ queryResults = await runnable.run({ rowLimit });
139118
+ } catch (error) {
139119
+ const errorEndTime = performance.now();
139120
+ const errorExecutionTime = errorEndTime - startTime;
139121
+ this.queryExecutionHistogram.record(errorExecutionTime, {
139122
+ "malloy.model.path": this.modelPath,
139123
+ "malloy.model.query.name": queryName,
139124
+ "malloy.model.query.source": sourceName,
139125
+ "malloy.model.query.query": query,
139126
+ "malloy.model.query.status": "error"
139127
+ });
139128
+ if (error instanceof import_malloy2.MalloyError) {
139129
+ throw error;
139130
+ }
139131
+ const errorMessage = error instanceof Error ? error.message : String(error);
139132
+ logger2.error("Query execution error", {
139133
+ error,
139134
+ errorMessage,
139135
+ projectName: this.packageName,
139136
+ modelPath: this.modelPath,
139137
+ query,
139138
+ queryName,
139139
+ sourceName
139140
+ });
139141
+ throw new BadRequestError(`Query execution failed: ${errorMessage}`);
139142
+ }
139067
139143
  this.queryExecutionHistogram.record(executionTime, {
139068
139144
  "malloy.model.path": this.modelPath,
139069
139145
  "malloy.model.query.name": queryName,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@malloy-publisher/server",
3
3
  "description": "Malloy Publisher Server",
4
- "version": "0.0.132",
4
+ "version": "0.0.134",
5
5
  "main": "dist/server.js",
6
6
  "bin": {
7
7
  "malloy-publisher": "dist/server.js"
@@ -61,6 +61,8 @@ function validateAndBuildTrinoConfig(
61
61
  },
62
62
  };
63
63
  delete baseConfig.password;
64
+ delete baseConfig.catalog;
65
+ delete baseConfig.schema;
64
66
  } else if (
65
67
  trinoConfig.server?.startsWith("https://") &&
66
68
  trinoConfig.password
@@ -188,10 +188,47 @@ export async function getSchemasForConnection(
188
188
  throw new Error("Trino connection is required");
189
189
  }
190
190
  try {
191
+ let result: unknown;
191
192
  // Use the connection's runSQL method to query schemas
192
- const result = await malloyConnection.runSQL(
193
- `SHOW SCHEMAS FROM ${connection.trinoConnection.catalog}`,
194
- );
193
+ if (connection.trinoConnection.catalog) {
194
+ result = await malloyConnection.runSQL(
195
+ `SHOW SCHEMAS FROM ${connection.trinoConnection.catalog}`,
196
+ );
197
+ } else {
198
+ const catalogs = await malloyConnection.runSQL(`SHOW CATALOGS`);
199
+ console.log("catalogs", catalogs);
200
+ let catalogNames = standardizeRunSQLResult(catalogs);
201
+ catalogNames = catalogNames.map((catalog: unknown) => {
202
+ const typedCatalog = catalog as Record<string, unknown>;
203
+ return typedCatalog.Catalog as string;
204
+ });
205
+
206
+ const schemas: unknown[] = [];
207
+
208
+ console.log("catalogNames", catalogNames);
209
+ for (const catalog of catalogNames) {
210
+ const schemasResult = await malloyConnection.runSQL(
211
+ `SHOW SCHEMAS FROM ${catalog}`,
212
+ );
213
+ const schemasResultRows = standardizeRunSQLResult(schemasResult);
214
+ console.log("schemasResultRows", schemasResultRows);
215
+
216
+ // Concat catalog name to schema name for each schema row
217
+ const schemasWithCatalog = schemasResultRows.map(
218
+ (row: unknown) => {
219
+ const typedRow = row as Record<string, unknown>;
220
+ // For display, use the convention "catalog.schema"
221
+ return {
222
+ ...typedRow,
223
+ Schema: `${catalog}.${typedRow.Schema ?? typedRow.schema ?? ""}`,
224
+ };
225
+ },
226
+ );
227
+ schemas.push(...schemasWithCatalog);
228
+ console.log("schemas", schemas);
229
+ }
230
+ result = schemas;
231
+ }
195
232
 
196
233
  const rows = standardizeRunSQLResult(result);
197
234
  return rows.map((row: unknown) => {
@@ -316,7 +353,12 @@ export async function getTablesForSchema(
316
353
  let tablePath: string;
317
354
 
318
355
  if (connection.type === "trino") {
319
- tablePath = `${connection.trinoConnection?.catalog}.${schemaName}.${tableName}`;
356
+ if (connection.trinoConnection?.catalog) {
357
+ tablePath = `${connection.trinoConnection?.catalog}.${schemaName}.${tableName}`;
358
+ } else {
359
+ // Catalog name is included in the schema name
360
+ tablePath = `${schemaName}.${tableName}`;
361
+ }
320
362
  } else {
321
363
  tablePath = `${schemaName}.${tableName}`;
322
364
  }
@@ -374,14 +416,18 @@ export async function getConnectionTableSource(
374
416
  }
375
417
  ).fetchTableSchema(tableKey, tablePath);
376
418
  if (source === undefined) {
377
- throw new ConnectionError(`Table ${tablePath} not found`);
419
+ throw new ConnectionError(
420
+ `Table ${tablePath} not found: ${JSON.stringify(source)}`,
421
+ );
378
422
  }
379
423
 
380
424
  // Validate that source has the expected structure
381
- if (!source || typeof source !== "object") {
425
+ if (!source) {
382
426
  throw new ConnectionError(
383
427
  `Invalid table source returned for ${tablePath}`,
384
428
  );
429
+ } else if (typeof source !== "object") {
430
+ throw new ConnectionError(JSON.stringify(source));
385
431
  }
386
432
 
387
433
  const malloyFields = (source as TableSourceDef).fields;
@@ -391,6 +437,13 @@ export async function getConnectionTableSource(
391
437
  );
392
438
  }
393
439
 
440
+ //This is for the Trino connection. The connection will not throw an error if the table is not found.
441
+ // Instead it will return an empty fields array. So we need to check for that.
442
+ // But it is fine to have it for all other connections as well.
443
+ if (malloyFields.length === 0) {
444
+ throw new ConnectionError(`Table ${tablePath} not found`);
445
+ }
446
+
394
447
  const fields = malloyFields.map((field) => {
395
448
  return {
396
449
  name: field.name,
@@ -406,8 +459,18 @@ export async function getConnectionTableSource(
406
459
  columns: fields,
407
460
  };
408
461
  } catch (error) {
409
- logger.error("fetchTableSchema error", { error, tableKey, tablePath });
410
- throw new ConnectionError((error as Error).message);
462
+ const errorMessage =
463
+ error instanceof Error
464
+ ? error.message
465
+ : typeof error === "string"
466
+ ? error
467
+ : JSON.stringify(error);
468
+ logger.error("fetchTableSchema error", {
469
+ error,
470
+ tableKey,
471
+ tablePath,
472
+ });
473
+ throw new ConnectionError(errorMessage);
411
474
  }
412
475
  }
413
476
 
@@ -505,9 +568,18 @@ export async function listTablesForSchema(
505
568
  throw new Error("Trino connection is required");
506
569
  }
507
570
  try {
508
- const result = await malloyConnection.runSQL(
509
- `SHOW TABLES FROM ${connection.trinoConnection.catalog}.${schemaName}`,
510
- );
571
+ let result: unknown;
572
+
573
+ if (connection.trinoConnection?.catalog) {
574
+ result = await malloyConnection.runSQL(
575
+ `SHOW TABLES FROM ${connection.trinoConnection.catalog}.${schemaName}`,
576
+ );
577
+ } else {
578
+ // Catalog name is included in the schema name
579
+ result = await malloyConnection.runSQL(
580
+ `SHOW TABLES FROM ${schemaName}`,
581
+ );
582
+ }
511
583
  const rows = standardizeRunSQLResult(result);
512
584
  return rows.map((row: unknown) => {
513
585
  const typedRow = row as Record<string, unknown>;
@@ -269,7 +269,42 @@ export class Model {
269
269
  (await runnable.getPreparedResult()).resultExplore.limit || ROW_LIMIT;
270
270
  const endTime = performance.now();
271
271
  const executionTime = endTime - startTime;
272
- const queryResults = await runnable.run({ rowLimit });
272
+
273
+ let queryResults;
274
+ try {
275
+ queryResults = await runnable.run({ rowLimit });
276
+ } catch (error) {
277
+ // Record error metrics
278
+ const errorEndTime = performance.now();
279
+ const errorExecutionTime = errorEndTime - startTime;
280
+ this.queryExecutionHistogram.record(errorExecutionTime, {
281
+ "malloy.model.path": this.modelPath,
282
+ "malloy.model.query.name": queryName,
283
+ "malloy.model.query.source": sourceName,
284
+ "malloy.model.query.query": query,
285
+ "malloy.model.query.status": "error",
286
+ });
287
+
288
+ // Re-throw Malloy errors as-is (they will be handled by error handler)
289
+ if (error instanceof MalloyError) {
290
+ throw error;
291
+ }
292
+
293
+ // For other runtime errors (like divide by zero), throw as BadRequestError
294
+ const errorMessage =
295
+ error instanceof Error ? error.message : String(error);
296
+ logger.error("Query execution error", {
297
+ error,
298
+ errorMessage,
299
+ projectName: this.packageName,
300
+ modelPath: this.modelPath,
301
+ query,
302
+ queryName,
303
+ sourceName,
304
+ });
305
+ throw new BadRequestError(`Query execution failed: ${errorMessage}`);
306
+ }
307
+
273
308
  this.queryExecutionHistogram.record(executionTime, {
274
309
  "malloy.model.path": this.modelPath,
275
310
  "malloy.model.query.name": queryName,