@sqg/sqg 0.21.0 → 0.21.1
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/index.mjs +49 -13
- package/dist/sqg.mjs +51 -15
- package/dist/templates/java-jdbc.hbs +3 -1
- package/dist/templates/python.hbs +7 -1
- package/dist/templates/typescript-duckdb.hbs +3 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
4
4
|
import { LRParser } from "@lezer/lr";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
6
|
import { basename, dirname, extname, join, relative, resolve } from "node:path";
|
|
7
|
-
import { camelCase, pascalCase, snakeCase } from "es-toolkit/string";
|
|
7
|
+
import { camelCase, escapeRegExp, pascalCase, snakeCase } from "es-toolkit/string";
|
|
8
8
|
import Handlebars from "handlebars";
|
|
9
9
|
import YAML from "yaml";
|
|
10
10
|
import { z } from "zod";
|
|
@@ -1931,8 +1931,20 @@ async function preparePostgresSources(sources, queries, reporter) {
|
|
|
1931
1931
|
};
|
|
1932
1932
|
try {
|
|
1933
1933
|
for (const source of sources) {
|
|
1934
|
+
if (source.url) {
|
|
1935
|
+
attachments.push({
|
|
1936
|
+
alias: source.name,
|
|
1937
|
+
connectionUri: source.url
|
|
1938
|
+
});
|
|
1939
|
+
continue;
|
|
1940
|
+
}
|
|
1934
1941
|
reporter?.onContainerStarting?.();
|
|
1935
|
-
|
|
1942
|
+
let container;
|
|
1943
|
+
try {
|
|
1944
|
+
container = await new PostgreSqlContainer(source.image).withDatabase("sqg-db").withUsername("sqg").withPassword("secret").start();
|
|
1945
|
+
} catch (e) {
|
|
1946
|
+
throw new DatabaseError(`Could not start a Postgres container for source '${source.name}': ${e.message}`, "postgres", "Postgres sources need Docker to introspect schema. Start Docker, or set 'url' on the source to point at an existing Postgres database (e.g. url: $DATABASE_URL).");
|
|
1947
|
+
}
|
|
1936
1948
|
containers.push(container);
|
|
1937
1949
|
const connectionUri = container.getConnectionUri();
|
|
1938
1950
|
reporter?.onContainerStarted?.(connectionUri);
|
|
@@ -2178,15 +2190,16 @@ const ProjectSchema = z.object({
|
|
|
2178
2190
|
template: z.string().optional().describe("Custom Handlebars template path"),
|
|
2179
2191
|
output: z.string().min(1, "Output path is required").describe("Output file or directory path"),
|
|
2180
2192
|
config: z.any().optional().describe("Generator-specific configuration")
|
|
2181
|
-
})).min(1, "At least one generator is required").describe("Code generators to run")
|
|
2182
|
-
})).min(1, "At least one SQL configuration is required").describe("SQL file configurations"),
|
|
2193
|
+
}).strict()).min(1, "At least one generator is required").describe("Code generators to run")
|
|
2194
|
+
}).strict()).min(1, "At least one SQL configuration is required").describe("SQL file configurations"),
|
|
2183
2195
|
sources: z.array(z.object({
|
|
2184
2196
|
type: z.enum(["file", "postgres"]).optional().describe("Source type: 'file' (default) or 'postgres'"),
|
|
2185
2197
|
path: z.string().optional().describe("Path to source file for file sources (supports $HOME)"),
|
|
2186
2198
|
name: z.string().optional().describe("Variable name (file sources) or DuckDB attach alias (postgres sources)"),
|
|
2187
|
-
image: z.string().optional().describe("Docker image for postgres sources (default: postgres:16-alpine)")
|
|
2188
|
-
|
|
2189
|
-
});
|
|
2199
|
+
image: z.string().optional().describe("Docker image for postgres sources (default: postgres:16-alpine)"),
|
|
2200
|
+
url: z.string().optional().describe("Postgres source: connect to this existing database for introspection instead of starting a container. Use a literal DSN or '$ENV_VAR' to read from the environment. When set, ':source=' BASELINE blocks are not applied (the live schema is used), which avoids schema drift.")
|
|
2201
|
+
}).strict().refine((s) => s.type === "postgres" ? !!s.name : !!s.path, { message: "file sources require 'path'; postgres sources require 'name'" })).optional().describe("External sources: files inlined as variables, or postgres databases attached for introspection")
|
|
2202
|
+
}).strict();
|
|
2190
2203
|
var ExtraVariable = class {
|
|
2191
2204
|
constructor(name, value) {
|
|
2192
2205
|
this.name = name;
|
|
@@ -2202,11 +2215,25 @@ function createExtraVariables(sources, suppressLogging = false) {
|
|
|
2202
2215
|
return new ExtraVariable(varName, `'${resolvedPath}'`);
|
|
2203
2216
|
});
|
|
2204
2217
|
}
|
|
2218
|
+
/**
|
|
2219
|
+
* Resolve a postgres source `url`. A value of the form `$VAR` or `${VAR}` is
|
|
2220
|
+
* read from the environment (so credentials need not live in sqg.yaml); any
|
|
2221
|
+
* other value is treated as a literal DSN.
|
|
2222
|
+
*/
|
|
2223
|
+
function resolveSourceUrl(url, sourceName) {
|
|
2224
|
+
const match = url.match(/^\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?$/);
|
|
2225
|
+
if (!match) return url;
|
|
2226
|
+
const envName = match[1];
|
|
2227
|
+
const value = process.env[envName];
|
|
2228
|
+
if (!value) throw new SqgError(`Postgres source '${sourceName}' references environment variable '${envName}' which is not set`, "CONFIG_VALIDATION_ERROR", `Set ${envName} to the database connection string, or use a literal DSN in the 'url' field`);
|
|
2229
|
+
return value;
|
|
2230
|
+
}
|
|
2205
2231
|
/** Postgres sources resolved from the project config (with image defaulted). */
|
|
2206
2232
|
function getPostgresSources(sources) {
|
|
2207
2233
|
return sources.filter((source) => source.type === "postgres").map((source) => ({
|
|
2208
2234
|
name: source.name,
|
|
2209
|
-
image: source.image ?? "postgres:16-alpine"
|
|
2235
|
+
image: source.image ?? "postgres:16-alpine",
|
|
2236
|
+
url: source.url ? resolveSourceUrl(source.url, source.name) : void 0
|
|
2210
2237
|
}));
|
|
2211
2238
|
}
|
|
2212
2239
|
function buildProjectFromCliOptions(options) {
|
|
@@ -2535,6 +2562,11 @@ async function processProjectFromConfig(project, projectDir, writeToStdout = fal
|
|
|
2535
2562
|
if (e instanceof SqgError) throw e;
|
|
2536
2563
|
throw SqgError.inFile(`Failed to parse SQL file: ${e.message}`, "SQL_PARSE_ERROR", sqlFile, { suggestion: "Check SQL syntax and annotation format" });
|
|
2537
2564
|
}
|
|
2565
|
+
const referencedPgSources = pgSources.filter((source) => {
|
|
2566
|
+
const catalogRef = new RegExp(`\\b${escapeRegExp(source.name)}\\.`);
|
|
2567
|
+
return queries.some((q) => q.sourceTarget === source.name || catalogRef.test(q.rawQuery));
|
|
2568
|
+
});
|
|
2569
|
+
const referencedPgSourceNames = referencedPgSources.map((s) => s.name);
|
|
2538
2570
|
for (const [engine, gens] of gensByEngine) {
|
|
2539
2571
|
const executableQueries = queries.filter((q) => !q.skipGenerateFunction);
|
|
2540
2572
|
const reporterAny = reporter;
|
|
@@ -2543,9 +2575,9 @@ async function processProjectFromConfig(project, projectDir, writeToStdout = fal
|
|
|
2543
2575
|
try {
|
|
2544
2576
|
const dbEngine = getDatabaseEngine(engine);
|
|
2545
2577
|
let attachments;
|
|
2546
|
-
if (engine === "duckdb" &&
|
|
2578
|
+
if (engine === "duckdb" && referencedPgSources.length > 0) {
|
|
2547
2579
|
ui?.startPhase("Starting postgres source containers...");
|
|
2548
|
-
preparedSources = await preparePostgresSources(
|
|
2580
|
+
preparedSources = await preparePostgresSources(referencedPgSources, queries, reporter);
|
|
2549
2581
|
attachments = preparedSources.attachments;
|
|
2550
2582
|
}
|
|
2551
2583
|
ui?.startPhase(`Initializing ${engine} database...`);
|
|
@@ -2576,7 +2608,7 @@ async function processProjectFromConfig(project, projectDir, writeToStdout = fal
|
|
|
2576
2608
|
const outputPath = await writeGeneratedFile(projectDir, {
|
|
2577
2609
|
...gen,
|
|
2578
2610
|
projectName: project.name
|
|
2579
|
-
}, generator, sqlFile, queries, tables, engine, writeToStdout,
|
|
2611
|
+
}, generator, sqlFile, queries, tables, engine, writeToStdout, referencedPgSourceNames);
|
|
2580
2612
|
if (outputPath !== null) {
|
|
2581
2613
|
files.push(outputPath);
|
|
2582
2614
|
results.push({
|
|
@@ -2813,8 +2845,12 @@ function parseSQLQueries(filePath, extraVariables) {
|
|
|
2813
2845
|
});
|
|
2814
2846
|
}
|
|
2815
2847
|
trim() {
|
|
2816
|
-
const
|
|
2817
|
-
|
|
2848
|
+
const isBlankString = (p) => typeof p === "string" && p.trim() === "";
|
|
2849
|
+
while (this.sqlParts.length > 0 && isBlankString(this.sqlParts[0])) this.sqlParts.shift();
|
|
2850
|
+
if (this.sqlParts.length > 0 && typeof this.sqlParts[0] === "string") this.sqlParts[0] = this.sqlParts[0].trimStart();
|
|
2851
|
+
while (this.sqlParts.length > 0 && isBlankString(this.sqlParts[this.sqlParts.length - 1])) this.sqlParts.pop();
|
|
2852
|
+
const lastIdx = this.sqlParts.length - 1;
|
|
2853
|
+
if (lastIdx >= 0 && typeof this.sqlParts[lastIdx] === "string") this.sqlParts[lastIdx] = this.sqlParts[lastIdx].trimEnd();
|
|
2818
2854
|
}
|
|
2819
2855
|
parameters() {
|
|
2820
2856
|
return this.sqlParts.filter((part) => typeof part !== "string" && !part.name.startsWith("sources_"));
|
package/dist/sqg.mjs
CHANGED
|
@@ -13,7 +13,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
13
13
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
14
14
|
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
15
15
|
import YAML from "yaml";
|
|
16
|
-
import { camelCase, pascalCase, snakeCase } from "es-toolkit/string";
|
|
16
|
+
import { camelCase, escapeRegExp, pascalCase, snakeCase } from "es-toolkit/string";
|
|
17
17
|
import Handlebars from "handlebars";
|
|
18
18
|
import { z } from "zod";
|
|
19
19
|
import { DuckDBEnumType, DuckDBInstance, DuckDBListType, DuckDBMapType, DuckDBStructType } from "@duckdb/node-api";
|
|
@@ -949,8 +949,12 @@ function parseSQLQueries(filePath, extraVariables) {
|
|
|
949
949
|
});
|
|
950
950
|
}
|
|
951
951
|
trim() {
|
|
952
|
-
const
|
|
953
|
-
|
|
952
|
+
const isBlankString = (p) => typeof p === "string" && p.trim() === "";
|
|
953
|
+
while (this.sqlParts.length > 0 && isBlankString(this.sqlParts[0])) this.sqlParts.shift();
|
|
954
|
+
if (this.sqlParts.length > 0 && typeof this.sqlParts[0] === "string") this.sqlParts[0] = this.sqlParts[0].trimStart();
|
|
955
|
+
while (this.sqlParts.length > 0 && isBlankString(this.sqlParts[this.sqlParts.length - 1])) this.sqlParts.pop();
|
|
956
|
+
const lastIdx = this.sqlParts.length - 1;
|
|
957
|
+
if (lastIdx >= 0 && typeof this.sqlParts[lastIdx] === "string") this.sqlParts[lastIdx] = this.sqlParts[lastIdx].trimEnd();
|
|
954
958
|
}
|
|
955
959
|
parameters() {
|
|
956
960
|
return this.sqlParts.filter((part) => typeof part !== "string" && !part.name.startsWith("sources_"));
|
|
@@ -3179,8 +3183,20 @@ async function preparePostgresSources(sources, queries, reporter) {
|
|
|
3179
3183
|
};
|
|
3180
3184
|
try {
|
|
3181
3185
|
for (const source of sources) {
|
|
3186
|
+
if (source.url) {
|
|
3187
|
+
attachments.push({
|
|
3188
|
+
alias: source.name,
|
|
3189
|
+
connectionUri: source.url
|
|
3190
|
+
});
|
|
3191
|
+
continue;
|
|
3192
|
+
}
|
|
3182
3193
|
reporter?.onContainerStarting?.();
|
|
3183
|
-
|
|
3194
|
+
let container;
|
|
3195
|
+
try {
|
|
3196
|
+
container = await new PostgreSqlContainer(source.image).withDatabase("sqg-db").withUsername("sqg").withPassword("secret").start();
|
|
3197
|
+
} catch (e) {
|
|
3198
|
+
throw new DatabaseError(`Could not start a Postgres container for source '${source.name}': ${e.message}`, "postgres", "Postgres sources need Docker to introspect schema. Start Docker, or set 'url' on the source to point at an existing Postgres database (e.g. url: $DATABASE_URL).");
|
|
3199
|
+
}
|
|
3184
3200
|
containers.push(container);
|
|
3185
3201
|
const connectionUri = container.getConnectionUri();
|
|
3186
3202
|
reporter?.onContainerStarted?.(connectionUri);
|
|
@@ -3426,15 +3442,16 @@ const ProjectSchema = z.object({
|
|
|
3426
3442
|
template: z.string().optional().describe("Custom Handlebars template path"),
|
|
3427
3443
|
output: z.string().min(1, "Output path is required").describe("Output file or directory path"),
|
|
3428
3444
|
config: z.any().optional().describe("Generator-specific configuration")
|
|
3429
|
-
})).min(1, "At least one generator is required").describe("Code generators to run")
|
|
3430
|
-
})).min(1, "At least one SQL configuration is required").describe("SQL file configurations"),
|
|
3445
|
+
}).strict()).min(1, "At least one generator is required").describe("Code generators to run")
|
|
3446
|
+
}).strict()).min(1, "At least one SQL configuration is required").describe("SQL file configurations"),
|
|
3431
3447
|
sources: z.array(z.object({
|
|
3432
3448
|
type: z.enum(["file", "postgres"]).optional().describe("Source type: 'file' (default) or 'postgres'"),
|
|
3433
3449
|
path: z.string().optional().describe("Path to source file for file sources (supports $HOME)"),
|
|
3434
3450
|
name: z.string().optional().describe("Variable name (file sources) or DuckDB attach alias (postgres sources)"),
|
|
3435
|
-
image: z.string().optional().describe("Docker image for postgres sources (default: postgres:16-alpine)")
|
|
3436
|
-
|
|
3437
|
-
});
|
|
3451
|
+
image: z.string().optional().describe("Docker image for postgres sources (default: postgres:16-alpine)"),
|
|
3452
|
+
url: z.string().optional().describe("Postgres source: connect to this existing database for introspection instead of starting a container. Use a literal DSN or '$ENV_VAR' to read from the environment. When set, ':source=' BASELINE blocks are not applied (the live schema is used), which avoids schema drift.")
|
|
3453
|
+
}).strict().refine((s) => s.type === "postgres" ? !!s.name : !!s.path, { message: "file sources require 'path'; postgres sources require 'name'" })).optional().describe("External sources: files inlined as variables, or postgres databases attached for introspection")
|
|
3454
|
+
}).strict();
|
|
3438
3455
|
var ExtraVariable = class {
|
|
3439
3456
|
constructor(name, value) {
|
|
3440
3457
|
this.name = name;
|
|
@@ -3450,11 +3467,25 @@ function createExtraVariables(sources, suppressLogging = false) {
|
|
|
3450
3467
|
return new ExtraVariable(varName, `'${resolvedPath}'`);
|
|
3451
3468
|
});
|
|
3452
3469
|
}
|
|
3470
|
+
/**
|
|
3471
|
+
* Resolve a postgres source `url`. A value of the form `$VAR` or `${VAR}` is
|
|
3472
|
+
* read from the environment (so credentials need not live in sqg.yaml); any
|
|
3473
|
+
* other value is treated as a literal DSN.
|
|
3474
|
+
*/
|
|
3475
|
+
function resolveSourceUrl(url, sourceName) {
|
|
3476
|
+
const match = url.match(/^\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?$/);
|
|
3477
|
+
if (!match) return url;
|
|
3478
|
+
const envName = match[1];
|
|
3479
|
+
const value = process.env[envName];
|
|
3480
|
+
if (!value) throw new SqgError(`Postgres source '${sourceName}' references environment variable '${envName}' which is not set`, "CONFIG_VALIDATION_ERROR", `Set ${envName} to the database connection string, or use a literal DSN in the 'url' field`);
|
|
3481
|
+
return value;
|
|
3482
|
+
}
|
|
3453
3483
|
/** Postgres sources resolved from the project config (with image defaulted). */
|
|
3454
3484
|
function getPostgresSources(sources) {
|
|
3455
3485
|
return sources.filter((source) => source.type === "postgres").map((source) => ({
|
|
3456
3486
|
name: source.name,
|
|
3457
|
-
image: source.image ?? "postgres:16-alpine"
|
|
3487
|
+
image: source.image ?? "postgres:16-alpine",
|
|
3488
|
+
url: source.url ? resolveSourceUrl(source.url, source.name) : void 0
|
|
3458
3489
|
}));
|
|
3459
3490
|
}
|
|
3460
3491
|
function buildProjectFromCliOptions(options) {
|
|
@@ -3783,6 +3814,11 @@ async function processProjectFromConfig(project, projectDir, writeToStdout = fal
|
|
|
3783
3814
|
if (e instanceof SqgError) throw e;
|
|
3784
3815
|
throw SqgError.inFile(`Failed to parse SQL file: ${e.message}`, "SQL_PARSE_ERROR", sqlFile, { suggestion: "Check SQL syntax and annotation format" });
|
|
3785
3816
|
}
|
|
3817
|
+
const referencedPgSources = pgSources.filter((source) => {
|
|
3818
|
+
const catalogRef = new RegExp(`\\b${escapeRegExp(source.name)}\\.`);
|
|
3819
|
+
return queries.some((q) => q.sourceTarget === source.name || catalogRef.test(q.rawQuery));
|
|
3820
|
+
});
|
|
3821
|
+
const referencedPgSourceNames = referencedPgSources.map((s) => s.name);
|
|
3786
3822
|
for (const [engine, gens] of gensByEngine) {
|
|
3787
3823
|
const executableQueries = queries.filter((q) => !q.skipGenerateFunction);
|
|
3788
3824
|
const reporterAny = reporter;
|
|
@@ -3791,9 +3827,9 @@ async function processProjectFromConfig(project, projectDir, writeToStdout = fal
|
|
|
3791
3827
|
try {
|
|
3792
3828
|
const dbEngine = getDatabaseEngine(engine);
|
|
3793
3829
|
let attachments;
|
|
3794
|
-
if (engine === "duckdb" &&
|
|
3830
|
+
if (engine === "duckdb" && referencedPgSources.length > 0) {
|
|
3795
3831
|
ui?.startPhase("Starting postgres source containers...");
|
|
3796
|
-
preparedSources = await preparePostgresSources(
|
|
3832
|
+
preparedSources = await preparePostgresSources(referencedPgSources, queries, reporter);
|
|
3797
3833
|
attachments = preparedSources.attachments;
|
|
3798
3834
|
}
|
|
3799
3835
|
ui?.startPhase(`Initializing ${engine} database...`);
|
|
@@ -3824,7 +3860,7 @@ async function processProjectFromConfig(project, projectDir, writeToStdout = fal
|
|
|
3824
3860
|
const outputPath = await writeGeneratedFile(projectDir, {
|
|
3825
3861
|
...gen,
|
|
3826
3862
|
projectName: project.name
|
|
3827
|
-
}, generator, sqlFile, queries, tables, engine, writeToStdout,
|
|
3863
|
+
}, generator, sqlFile, queries, tables, engine, writeToStdout, referencedPgSourceNames);
|
|
3828
3864
|
if (outputPath !== null) {
|
|
3829
3865
|
files.push(outputPath);
|
|
3830
3866
|
results.push({
|
|
@@ -3858,7 +3894,7 @@ async function processProject(projectPath, ui) {
|
|
|
3858
3894
|
//#region src/mcp-server.ts
|
|
3859
3895
|
const server = new Server({
|
|
3860
3896
|
name: "sqg-mcp",
|
|
3861
|
-
version: process.env.npm_package_version ?? "0.21.
|
|
3897
|
+
version: process.env.npm_package_version ?? "0.21.1"
|
|
3862
3898
|
}, { capabilities: {
|
|
3863
3899
|
tools: {},
|
|
3864
3900
|
resources: {}
|
|
@@ -4329,7 +4365,7 @@ function formatMs(ms) {
|
|
|
4329
4365
|
}
|
|
4330
4366
|
//#endregion
|
|
4331
4367
|
//#region src/sqg.ts
|
|
4332
|
-
const version = process.env.npm_package_version ?? "0.21.
|
|
4368
|
+
const version = process.env.npm_package_version ?? "0.21.1";
|
|
4333
4369
|
updateNotifier({ pkg: {
|
|
4334
4370
|
name: "@sqg/sqg",
|
|
4335
4371
|
version
|
|
@@ -224,9 +224,11 @@ public class {{className}} {
|
|
|
224
224
|
{{{declareEnums queries}}}
|
|
225
225
|
|
|
226
226
|
{{#each attachers}}
|
|
227
|
-
/** Attach the "{{alias}}" postgres source under the same alias used during generation. */
|
|
227
|
+
/** Attach the "{{alias}}" postgres source under the same alias used during generation. Loads the postgres extension (idempotent) so callers need not rely on DuckDB autoloading. */
|
|
228
228
|
public void {{functionName}}(String connectionString) throws SQLException {
|
|
229
229
|
try (var stmt = connection.createStatement()) {
|
|
230
|
+
stmt.execute("INSTALL postgres");
|
|
231
|
+
stmt.execute("LOAD postgres");
|
|
230
232
|
stmt.execute("ATTACH '" + connectionString + "' AS {{alias}} (TYPE postgres)");
|
|
231
233
|
}
|
|
232
234
|
}
|
|
@@ -137,7 +137,13 @@ class {{className}}:
|
|
|
137
137
|
|
|
138
138
|
{{#each attachers}}
|
|
139
139
|
def {{functionName}}(self, connection_string: str) -> None:
|
|
140
|
-
"""Attach the "{{alias}}" postgres source under the same alias used during generation.
|
|
140
|
+
"""Attach the "{{alias}}" postgres source under the same alias used during generation.
|
|
141
|
+
|
|
142
|
+
Loads the postgres extension (idempotent) so callers need not rely on
|
|
143
|
+
DuckDB autoloading.
|
|
144
|
+
"""
|
|
145
|
+
self._conn.execute("INSTALL postgres")
|
|
146
|
+
self._conn.execute("LOAD postgres")
|
|
141
147
|
self._conn.execute(f"ATTACH '{connection_string}' AS {{alias}} (TYPE postgres)")
|
|
142
148
|
|
|
143
149
|
{{/each}}
|
|
@@ -59,8 +59,10 @@ export class {{className}} {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
{{#each attachers}}
|
|
62
|
-
/** Attach the "{{alias}}" postgres source under the same alias used during generation. */
|
|
62
|
+
/** Attach the "{{alias}}" postgres source under the same alias used during generation. Loads the postgres extension (idempotent) so callers need not rely on DuckDB autoloading. */
|
|
63
63
|
async {{functionName}}(connectionString: string): Promise<void> {
|
|
64
|
+
await this.conn.run("INSTALL postgres");
|
|
65
|
+
await this.conn.run("LOAD postgres");
|
|
64
66
|
await this.conn.run(`ATTACH '${connectionString}' AS {{alias}} (TYPE postgres)`);
|
|
65
67
|
}
|
|
66
68
|
|