@malloy-publisher/server 0.0.123 → 0.0.125

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/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.123",
4
+ "version": "0.0.125",
5
5
  "main": "dist/server.js",
6
6
  "bin": {
7
7
  "malloy-publisher": "dist/server.js"
@@ -23,15 +23,15 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@google-cloud/storage": "^7.16.0",
26
- "@malloydata/db-bigquery": "^0.0.310",
27
- "@malloydata/db-duckdb": "^0.0.310",
28
- "@malloydata/db-mysql": "^0.0.310",
29
- "@malloydata/db-postgres": "^0.0.310",
30
- "@malloydata/db-snowflake": "^0.0.310",
31
- "@malloydata/db-trino": "^0.0.310",
32
- "@malloydata/malloy": "^0.0.310",
33
- "@malloydata/malloy-sql": "^0.0.310",
34
- "@malloydata/render": "^0.0.310",
26
+ "@malloydata/db-bigquery": "^0.0.318",
27
+ "@malloydata/db-duckdb": "^0.0.318",
28
+ "@malloydata/db-mysql": "^0.0.318",
29
+ "@malloydata/db-postgres": "^0.0.318",
30
+ "@malloydata/db-snowflake": "^0.0.318",
31
+ "@malloydata/db-trino": "^0.0.318",
32
+ "@malloydata/malloy": "^0.0.318",
33
+ "@malloydata/malloy-sql": "^0.0.318",
34
+ "@malloydata/render": "^0.0.318",
35
35
  "@modelcontextprotocol/sdk": "^1.13.2",
36
36
  "@opentelemetry/api": "^1.9.0",
37
37
  "@opentelemetry/auto-instrumentations-node": "^0.57.0",
@@ -40,9 +40,7 @@ export class ConnectionController {
40
40
  if (connection.type === "duckdb") {
41
41
  const packages = await project.listPackages();
42
42
  if (packages.length === 0) {
43
- throw new ConnectionError(
44
- "No packages found for DuckDB connection",
45
- );
43
+ return project.getMalloyConnection(connectionName);
46
44
  }
47
45
  // For now, use the first package's DuckDB connection
48
46
  const packageName = packages[0].name;
@@ -165,6 +163,7 @@ export class ConnectionController {
165
163
  return {
166
164
  resource: tablePath,
167
165
  columns: tableSource.columns,
166
+ source: tableSource.source,
168
167
  };
169
168
  }
170
169
 
@@ -301,6 +301,7 @@ async function attachPostgres(
301
301
  if (config.databaseName) parts.push(`dbname=${config.databaseName}`);
302
302
  if (config.userName) parts.push(`user=${config.userName}`);
303
303
  if (config.password) parts.push(`password=${config.password}`);
304
+ if (process.env.PGSSLMODE === "no-verify") parts.push(`sslmode=disable`);
304
305
  attachString = parts.join(" ");
305
306
  }
306
307
 
@@ -309,42 +310,6 @@ async function attachPostgres(
309
310
  logger.info(`Successfully attached PostgreSQL database: ${attachedDb.name}`);
310
311
  }
311
312
 
312
- async function attachMotherDuck(
313
- connection: DuckDBConnection,
314
- attachedDb: AttachedDatabase,
315
- ): Promise<void> {
316
- if (!attachedDb.motherDuckConnection) {
317
- throw new Error(
318
- `MotherDuck connection configuration missing for: ${attachedDb.name}`,
319
- );
320
- }
321
-
322
- const config = attachedDb.motherDuckConnection;
323
-
324
- if (!config.database) {
325
- throw new Error(
326
- `MotherDuck database name is required for: ${attachedDb.name}`,
327
- );
328
- }
329
-
330
- await installAndLoadExtension(connection, "motherduck");
331
-
332
- // Set token if provided
333
- if (config.accessToken) {
334
- const escapedToken = escapeSQL(config.accessToken);
335
- await connection.runSQL(`SET motherduck_token = '${escapedToken}';`);
336
- }
337
-
338
- const connectionString = `md:${config.database}`;
339
- logger.info(
340
- `Connecting to MotherDuck database: ${config.database} as ${attachedDb.name}`,
341
- );
342
-
343
- const attachCommand = `ATTACH '${connectionString}' AS ${attachedDb.name} (TYPE motherduck, READ_ONLY);`;
344
- await connection.runSQL(attachCommand);
345
- logger.info(`Successfully attached MotherDuck database: ${attachedDb.name}`);
346
- }
347
-
348
313
  // Main attachment function
349
314
  async function attachDatabasesToDuckDB(
350
315
  duckdbConnection: DuckDBConnection,
@@ -354,7 +319,6 @@ async function attachDatabasesToDuckDB(
354
319
  bigquery: attachBigQuery,
355
320
  snowflake: attachSnowflake,
356
321
  postgres: attachPostgres,
357
- motherduck: attachMotherDuck,
358
322
  };
359
323
 
360
324
  for (const attachedDb of attachedDatabases) {
@@ -595,6 +559,37 @@ export async function createProjectConnections(
595
559
  break;
596
560
  }
597
561
 
562
+ case "motherduck": {
563
+ if (!connection.motherduckConnection) {
564
+ throw new Error(
565
+ "MotherDuck connection configuration is missing.",
566
+ );
567
+ }
568
+
569
+ if (!connection.motherduckConnection.accessToken) {
570
+ throw new Error("MotherDuck access token is required.");
571
+ }
572
+
573
+ let databasePath = `md:`;
574
+ // Build the MotherDuck database path
575
+ if (connection.motherduckConnection.database) {
576
+ databasePath = `md:${connection.motherduckConnection.database}?attach_mode=single`;
577
+ }
578
+
579
+ // Create MotherDuck connection using DuckDBConnectionOptions interface
580
+ const motherduckConnection = new DuckDBConnection({
581
+ name: connection.name,
582
+ databasePath: databasePath,
583
+ motherDuckToken: connection.motherduckConnection.accessToken,
584
+ workingDirectory: projectPath,
585
+ });
586
+
587
+ connectionMap.set(connection.name, motherduckConnection);
588
+ connection.attributes =
589
+ getConnectionAttributes(motherduckConnection);
590
+ break;
591
+ }
592
+
598
593
  default: {
599
594
  throw new Error(`Unsupported connection type: ${connection.type}`);
600
595
  }
@@ -737,12 +732,6 @@ export async function testConnectionConfig(
737
732
 
738
733
  // Use createProjectConnections to create the connection, then test it
739
734
  // TODO: Test duckdb connections?
740
- if (connectionConfig.type === "duckdb") {
741
- return {
742
- status: "ok",
743
- errorMessage: "",
744
- };
745
- }
746
735
 
747
736
  const { malloyConnections } = await createProjectConnections(
748
737
  [connectionConfig], // Pass the single connection config
@@ -80,11 +80,20 @@ export async function getSchemasForConnection(
80
80
  const bigquery = createBigQueryClient(connection);
81
81
  const [datasets] = await bigquery.getDatasets();
82
82
 
83
- return datasets.map((dataset) => ({
84
- name: dataset.id,
85
- isHidden: false,
86
- isDefault: false,
87
- }));
83
+ const schemas = await Promise.all(
84
+ datasets.map(async (dataset) => {
85
+ const [metadata] = await dataset.getMetadata();
86
+ return {
87
+ name: dataset.id,
88
+ isHidden: false,
89
+ isDefault: false,
90
+ // Include description from dataset metadata if available
91
+ description: (metadata as { description?: string })
92
+ ?.description,
93
+ };
94
+ }),
95
+ );
96
+ return schemas;
88
97
  } catch (error) {
89
98
  console.error(
90
99
  `Error getting schemas for BigQuery connection ${connection.name}:`,
@@ -251,6 +260,39 @@ export async function getSchemasForConnection(
251
260
  `Failed to get schemas for DuckDB connection ${connection.name}: ${(error as Error).message}`,
252
261
  );
253
262
  }
263
+ } else if (connection.type === "motherduck") {
264
+ if (!connection.motherduckConnection) {
265
+ throw new Error("MotherDuck connection is required");
266
+ }
267
+ try {
268
+ // Use MotherDuck's INFORMATION_SCHEMA.SCHEMATA to list schemas
269
+ const result = await malloyConnection.runSQL(
270
+ "SELECT DISTINCT schema_name as row FROM information_schema.schemata ORDER BY schema_name",
271
+ { rowLimit: 1000 },
272
+ );
273
+ const rows = standardizeRunSQLResult(result);
274
+ console.log(rows);
275
+ return rows.map((row: unknown) => {
276
+ const typedRow = row as { row: string };
277
+ return {
278
+ name: typedRow.row,
279
+ isHidden: [
280
+ "information_schema",
281
+ "performance_schema",
282
+ "",
283
+ ].includes(typedRow.row),
284
+ isDefault: false,
285
+ };
286
+ });
287
+ } catch (error) {
288
+ console.error(
289
+ `Error getting schemas for MotherDuck connection ${connection.name}:`,
290
+ error,
291
+ );
292
+ throw new Error(
293
+ `Failed to get schemas for MotherDuck connection ${connection.name}: ${(error as Error).message}`,
294
+ );
295
+ }
254
296
  } else {
255
297
  throw new Error(`Unsupported connection type: ${connection.type}`);
256
298
  }
@@ -500,8 +542,30 @@ export async function listTablesForSchema(
500
542
  `Failed to get tables for DuckDB schema ${schemaName} in connection ${connection.name}: ${(error as Error).message}`,
501
543
  );
502
544
  }
545
+ } else if (connection.type === "motherduck") {
546
+ if (!connection.motherduckConnection) {
547
+ throw new Error("MotherDuck connection is required");
548
+ }
549
+ try {
550
+ const result = await malloyConnection.runSQL(
551
+ `SELECT table_name as row FROM information_schema.tables WHERE table_schema = '${schemaName}' ORDER BY table_name`,
552
+ { rowLimit: 1000 },
553
+ );
554
+ const rows = standardizeRunSQLResult(result);
555
+ return rows.map((row: unknown) => {
556
+ const typedRow = row as { row: string };
557
+ return typedRow.row;
558
+ });
559
+ } catch (error) {
560
+ logger.error(
561
+ `Error getting tables for MotherDuck schema ${schemaName} in connection ${connection.name}`,
562
+ { error },
563
+ );
564
+ throw new Error(
565
+ `Failed to get tables for MotherDuck schema ${schemaName} in connection ${connection.name}: ${(error as Error).message}`,
566
+ );
567
+ }
503
568
  } else {
504
- // TODO(jjs) - implement
505
- return [];
569
+ throw new Error(`Unsupported connection type: ${connection.type}`);
506
570
  }
507
571
  }