From 52f8de55e6b191267118e9bf637fc855ffb47571 Mon Sep 17 00:00:00 2001 From: Xetera Date: Fri, 30 Jan 2026 16:58:50 +0300 Subject: [PATCH 1/6] expose port 5432 in dockerfiles --- Dockerfile | 2 ++ pg14.Dockerfile | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Dockerfile b/Dockerfile index 1456b15..c300c4d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -131,6 +131,8 @@ RUN sed -i 's|nobody:/|nobody:/home|' /etc/passwd && chown nobody:nobody /home ENV POSTGRES_URL=postgresql://postgres@localhost/postgres?host=/tmp +EXPOSE 5432 + # Development command - starts both PostgreSQL and the analyzer CMD ["/bin/bash", "-c", "\ su-exec postgres initdb -D $PGDATA || true && \ diff --git a/pg14.Dockerfile b/pg14.Dockerfile index 2b83951..8a184b8 100644 --- a/pg14.Dockerfile +++ b/pg14.Dockerfile @@ -145,6 +145,8 @@ RUN su-exec postgres initdb -D $PGDATA || true && \ USER postgres +EXPOSE 2345 + CMD ["/bin/bash", "-c", "\ pg_ctl -D $PGDATA -l $PGDATA/logfile start || (cat $PGDATA/logfile && exit 1) && \ until pg_isready -h /tmp; do sleep 0.5; done && \ From 6f67f3f3ae28939393e5a7a608c3c022b29fa0df Mon Sep 17 00:00:00 2001 From: Xetera Date: Fri, 30 Jan 2026 17:07:48 +0300 Subject: [PATCH 2/6] feat: add `waitedMs` and `retries` to timed out queries --- src/remote/optimization.ts | 11 ++++++-- src/remote/query-optimizer.ts | 53 ++++++++++++++++++++++++++++------- 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/remote/optimization.ts b/src/remote/optimization.ts index f2d7d46..812a7c5 100644 --- a/src/remote/optimization.ts +++ b/src/remote/optimization.ts @@ -19,7 +19,10 @@ export const LiveQueryOptimization = z.discriminatedUnion("state", [ z.object({ state: z.literal("waiting"), }), - z.object({ state: z.literal("optimizing") }), + z.object({ + state: z.literal("optimizing"), + retries: z.number().nonnegative(), + }), z.object({ state: z.literal("not_supported"), reason: z.string() }), z.object({ state: z.literal("improvements_available"), @@ -37,7 +40,11 @@ export const LiveQueryOptimization = z.discriminatedUnion("state", [ indexesUsed: z.array(z.string()), explainPlan: z.custom(), }), - z.object({ state: z.literal("timeout") }), + z.object({ + state: z.literal("timeout"), + waitedMs: z.number(), + retries: z.number().nonnegative(), + }), z.object({ state: z.literal("error"), error: z.string(), diff --git a/src/remote/query-optimizer.ts b/src/remote/query-optimizer.ts index 39e29db..5eeea66 100644 --- a/src/remote/query-optimizer.ts +++ b/src/remote/query-optimizer.ts @@ -61,6 +61,7 @@ export class QueryOptimizer extends EventEmitter { private queriedSinceVacuum = 0; private static readonly vacuumThreshold = 5; + static MAX_RETRIES = 3; constructor( private readonly manager: ConnectionManager, @@ -241,16 +242,15 @@ export class QueryOptimizer extends EventEmitter { let optimized: OptimizedQuery | undefined; const token = await this.semaphore.acquire(); try { - for (const [hash, entry] of this.queries.entries()) { - if (entry.optimization.state !== "waiting") { - continue; - } - this.queries.set( - hash, - entry.withOptimization({ state: "optimizing" }), + optimized = this.findAndMarkFirstQueryWhere((entry) => + entry.optimization.state === "waiting" + ); + // if nothing is in queue, start working through timed-out queries + if (!optimized) { + optimized = this.findAndMarkFirstQueryWhere((entry) => + entry.optimization.state === "timeout" && + this.isEligibleForTimeoutRetry(entry) ); - optimized = entry; - break; } } finally { this.semaphore.release(token); @@ -280,6 +280,15 @@ export class QueryOptimizer extends EventEmitter { } } + private isEligibleForTimeoutRetry( + entry: OptimizedQuery, + ): boolean { + if (entry.optimization.state !== "timeout") { + return false; + } + return entry.optimization.retries < QueryOptimizer.MAX_RETRIES; + } + /** * Gets the status of the current queries in the optimizer */ @@ -287,6 +296,25 @@ export class QueryOptimizer extends EventEmitter { return Array.from(this.queries.values()); } + private findAndMarkFirstQueryWhere( + filter: (query: OptimizedQuery) => boolean, + ): OptimizedQuery | undefined { + for (const [hash, entry] of this.queries.entries()) { + if (!filter(entry)) { + continue; + } + let retries = 0; + if (entry.optimization.state === "timeout") { + retries = entry.optimization.retries; + } + this.queries.set( + hash, + entry.withOptimization({ state: "optimizing", retries }), + ); + return entry; + } + } + private async vacuum() { const connector = this.manager.getConnectorFor(this.connectable); try { @@ -544,8 +572,13 @@ export class QueryOptimizer extends EventEmitter { recent: OptimizedQuery, waitedMs: number, ): LiveQueryOptimization { + let retries = 0; + // increment retries if query was already timed out + if (recent.optimization.state === "timeout") { + retries = recent.optimization.retries + 1; + } this.emit("timeout", recent, waitedMs); - return { state: "timeout" }; + return { state: "timeout", waitedMs, retries }; } } From 4de6905e6ffeca90e383894d200506301a54b313 Mon Sep 17 00:00:00 2001 From: Xetera Date: Fri, 30 Jan 2026 17:40:03 +0300 Subject: [PATCH 3/6] feat: scale timeout amount based on past retries --- src/remote/query-optimizer.ts | 57 ++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/src/remote/query-optimizer.ts b/src/remote/query-optimizer.ts index 5eeea66..202cbd1 100644 --- a/src/remote/query-optimizer.ts +++ b/src/remote/query-optimizer.ts @@ -23,7 +23,6 @@ import { parse } from "@libpg-query/parser"; import { DisabledIndexes } from "./disabled-indexes.ts"; const MINIMUM_COST_CHANGE_PERCENTAGE = 5; -const QUERY_TIMEOUT_MS = 10000; type EventMap = { error: [Error, OptimizedQuery]; @@ -61,13 +60,23 @@ export class QueryOptimizer extends EventEmitter { private queriedSinceVacuum = 0; private static readonly vacuumThreshold = 5; - static MAX_RETRIES = 3; + private readonly maxRetries: number; + private readonly queryTimeoutMs: number; + private readonly queryTimeoutMaxMs: number; constructor( private readonly manager: ConnectionManager, private readonly connectable: Connectable, + config?: { + maxRetries?: number; + queryTimeoutMs?: number; + queryTimeoutMaxMs?: number; + }, ) { super(); + this.maxRetries = config?.maxRetries ?? 3; + this.queryTimeoutMs = config?.queryTimeoutMs ?? 10_000; + this.queryTimeoutMaxMs = config?.queryTimeoutMaxMs ?? 120_000; } get validQueriesProcessed() { @@ -263,6 +272,7 @@ export class QueryOptimizer extends EventEmitter { const optimization = await this.optimizeQuery( optimized, this.target, + { timeoutMs: this.calculateTimeoutRetryDelay(optimized) }, ); this.queriedSinceVacuum++; if (this.queriedSinceVacuum > QueryOptimizer.vacuumThreshold) { @@ -280,15 +290,6 @@ export class QueryOptimizer extends EventEmitter { } } - private isEligibleForTimeoutRetry( - entry: OptimizedQuery, - ): boolean { - if (entry.optimization.state !== "timeout") { - return false; - } - return entry.optimization.retries < QueryOptimizer.MAX_RETRIES; - } - /** * Gets the status of the current queries in the optimizer */ @@ -315,6 +316,27 @@ export class QueryOptimizer extends EventEmitter { } } + private calculateTimeoutRetryDelay(entry: OptimizedQuery): number { + const baseDelay = this.queryTimeoutMs; + let delay = baseDelay; + if (entry.optimization.state === "timeout") { + delay = Math.min( + baseDelay * Math.pow(2, entry.optimization.retries), + this.queryTimeoutMaxMs, + ); + } + return delay; + } + + private isEligibleForTimeoutRetry( + entry: OptimizedQuery, + ): boolean { + if (entry.optimization.state !== "timeout") { + return false; + } + return entry.optimization.retries < this.maxRetries; + } + private async vacuum() { const connector = this.manager.getConnectorFor(this.connectable); try { @@ -357,7 +379,7 @@ export class QueryOptimizer extends EventEmitter { private async optimizeQuery( recent: OptimizedQuery, target: Target, - timeoutMs = QUERY_TIMEOUT_MS, + options: { timeoutMs: number }, ): Promise { const builder = new PostgresQueryBuilder(recent.query); let cost: number; @@ -365,14 +387,14 @@ export class QueryOptimizer extends EventEmitter { try { const explain = await withTimeout( target.optimizer.testQueryWithStats(builder), - timeoutMs, + options.timeoutMs, ); cost = explain.Plan["Total Cost"]; explainPlan = explain.Plan; } catch (error) { console.error("Error with baseline run", error); if (error instanceof TimeoutError) { - return this.onTimeout(recent, timeoutMs); + return this.onTimeout(recent, options.timeoutMs); } else if (error instanceof Error) { return this.onError(recent, error.message); } else { @@ -394,12 +416,12 @@ export class QueryOptimizer extends EventEmitter { indexes, (tx) => this.dropDisabledIndexes(tx), ), - timeoutMs, + options.timeoutMs, ); } catch (error) { console.error("Error with optimization", error); if (error instanceof TimeoutError) { - return this.onTimeout(recent, timeoutMs); + return this.onTimeout(recent, options.timeoutMs); } else if (error instanceof Error) { return this.onError(recent, error.message, explainPlan); } else { @@ -573,8 +595,7 @@ export class QueryOptimizer extends EventEmitter { waitedMs: number, ): LiveQueryOptimization { let retries = 0; - // increment retries if query was already timed out - if (recent.optimization.state === "timeout") { + if ("retries" in recent.optimization) { retries = recent.optimization.retries + 1; } this.emit("timeout", recent, waitedMs); From 17f37a3e3d85441522528a528f8fc2bc37e3be40 Mon Sep 17 00:00:00 2001 From: Xetera Date: Fri, 30 Jan 2026 20:48:52 +0300 Subject: [PATCH 4/6] chore: add tests for timeout retries --- src/remote/query-optimizer.test.ts | 100 ++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/src/remote/query-optimizer.test.ts b/src/remote/query-optimizer.test.ts index 630d761..59b2056 100644 --- a/src/remote/query-optimizer.test.ts +++ b/src/remote/query-optimizer.test.ts @@ -4,7 +4,7 @@ import { ConnectionManager } from "../sync/connection-manager.ts"; import { Connectable } from "../sync/connectable.ts"; import { setTimeout } from "node:timers/promises"; import { assertArrayIncludes } from "@std/assert/array-includes"; -import { assert, assertGreater } from "@std/assert"; +import { assert, assertEquals, assertGreater } from "@std/assert"; import { type OptimizedQuery, RecentQuery } from "../sql/recent-query.ts"; Deno.test({ @@ -446,3 +446,101 @@ Deno.test({ } }, }); + +Deno.test({ + name: + "timed out queries are retried with exponential backoff up to maxRetries", + sanitizeOps: false, + sanitizeResources: false, + fn: async () => { + const pg = await new PostgreSqlContainer("postgres:17") + .withCopyContentToContainer([ + { + content: ` + create table slow_table(id int, data text); + insert into slow_table (id, data) select i, repeat('x', 1000) from generate_series(1, 100) i; + create extension pg_stat_statements; + select * from slow_table where id = 1; + `, + target: "/docker-entrypoint-initdb.d/init.sql", + }, + ]) + .withCommand([ + "-c", + "shared_preload_libraries=pg_stat_statements", + "-c", + "autovacuum=off", + "-c", + "track_counts=off", + "-c", + "track_io_timing=off", + "-c", + "track_activities=off", + ]) + .start(); + + const maxRetries = 2; + const queryTimeoutMs = 1; + + const manager = ConnectionManager.forLocalDatabase(); + const conn = Connectable.fromString(pg.getConnectionUri()); + const optimizer = new QueryOptimizer(manager, conn, { + maxRetries, + queryTimeoutMs, + queryTimeoutMaxMs: 100, + }); + + const timeoutEvents: { query: OptimizedQuery; waitedMs: number }[] = []; + optimizer.addListener("timeout", (query, waitedMs) => { + timeoutEvents.push({ query, waitedMs }); + }); + + const connector = manager.getConnectorFor(conn); + try { + const recentQueries = await connector.getRecentQueries(); + const slowQuery = recentQueries.find((q) => + q.query.includes("slow_table") && q.query.startsWith("select") + ); + assert(slowQuery, "Expected to find slow_table query"); + + await optimizer.start([slowQuery], { + kind: "fromStatisticsExport", + source: { kind: "inline" }, + stats: [{ + tableName: "slow_table", + schemaName: "public", + relpages: 1000, + reltuples: 1_000_000, + relallvisible: 1, + columns: [ + { columnName: "id", stats: null }, + { columnName: "data", stats: null }, + ], + indexes: [], + }], + }); + + await optimizer.finish; + + const queries = optimizer.getQueries(); + const resultQuery = queries.find((q) => q.query.includes("slow_table")); + assert(resultQuery, "Expected slow_table query in results"); + + assertEquals( + resultQuery.optimization.state, + "timeout", + "Expected query to be in timeout state", + ); + + if (resultQuery.optimization.state === "timeout") { + assertEquals( + resultQuery.optimization.retries, + maxRetries, + `Expected ${maxRetries} retries`, + ); + } + } finally { + await pg.stop(); + } + }, +}); From e56334794e96972c2d390a655ad0e0ea178213da Mon Sep 17 00:00:00 2001 From: Xetera Date: Fri, 30 Jan 2026 20:52:50 +0300 Subject: [PATCH 5/6] feat: reduce base timeout to 5s --- src/remote/query-optimizer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/remote/query-optimizer.ts b/src/remote/query-optimizer.ts index 202cbd1..a54f3be 100644 --- a/src/remote/query-optimizer.ts +++ b/src/remote/query-optimizer.ts @@ -74,8 +74,8 @@ export class QueryOptimizer extends EventEmitter { }, ) { super(); - this.maxRetries = config?.maxRetries ?? 3; - this.queryTimeoutMs = config?.queryTimeoutMs ?? 10_000; + this.maxRetries = config?.maxRetries ?? 5; + this.queryTimeoutMs = config?.queryTimeoutMs ?? 5_000; this.queryTimeoutMaxMs = config?.queryTimeoutMaxMs ?? 120_000; } From 7757cefa88e74957fe8ebc3a8a8c99d131a44524 Mon Sep 17 00:00:00 2001 From: Xetera Date: Mon, 2 Feb 2026 18:52:52 +0300 Subject: [PATCH 6/6] fix: downgrade postgresjs to 3.4.3 to fix a bug https://github.com/porsager/postgres/issues/1154 --- deno.json | 2 +- deno.lock | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/deno.json b/deno.json index 61e1761..96b58d2 100644 --- a/deno.json +++ b/deno.json @@ -45,7 +45,7 @@ "prettier": "npm:prettier@^3", "prettier-plugin-sql": "npm:prettier-plugin-sql@^0.19", "sql-highlight": "npm:sql-highlight@^6.1.0", - "postgresjs": "https://deno.land/x/postgresjs@v3.4.7/mod.js", + "postgresjs": "https://deno.land/x/postgresjs@v3.4.3/mod.js", "zod": "npm:zod@^4.1.12" } } diff --git a/deno.lock b/deno.lock index 7e0ceb7..0b5a2e2 100644 --- a/deno.lock +++ b/deno.lock @@ -1655,6 +1655,78 @@ "https://deno.land/std@0.224.0/path/windows/resolve.ts": "8dae1dadfed9d46ff46cc337c9525c0c7d959fb400a6308f34595c45bdca1972", "https://deno.land/std@0.224.0/path/windows/to_file_url.ts": "40e560ee4854fe5a3d4d12976cef2f4e8914125c81b11f1108e127934ced502e", "https://deno.land/std@0.224.0/path/windows/to_namespaced_path.ts": "4ffa4fb6fae321448d5fe810b3ca741d84df4d7897e61ee29be961a6aac89a4c", + "https://deno.land/x/postgresjs@v3.4.0/mod.js": "cb68f17d6d90df318934deccdb469d740be0888e7a597a9e7eea7100ce36a252", + "https://deno.land/x/postgresjs@v3.4.0/polyfills.js": "318eb01f2b4cc33a46c59f3ddc11f22a56d6b1db8b7719b2ad7decee63a5bd47", + "https://deno.land/x/postgresjs@v3.4.0/src/bytes.js": "f2de43bdc8fa5dc4b169f2c70d5d8b053a3dea8f85ef011d7b27dec69e14ebb7", + "https://deno.land/x/postgresjs@v3.4.0/src/connection.js": "3f22eea8d1150e4c3310b41bfe4477beb1063051042fccc25500c01d7b23648e", + "https://deno.land/x/postgresjs@v3.4.0/src/errors.js": "85cfbed9a5ab0db41ab8e97b806c881af29807dfe99bc656fdf1a18c1c13b6c6", + "https://deno.land/x/postgresjs@v3.4.0/src/index.js": "ae897f92a0913e2b05b1f706f07bca35ace89df3ab4e7382b613e1469c1e1800", + "https://deno.land/x/postgresjs@v3.4.0/src/large.js": "f3e770cdb7cc695f7b50687b4c6c4b7252129515486ec8def98b7582ee7c54ef", + "https://deno.land/x/postgresjs@v3.4.0/src/query.js": "67c45a5151032aa46b587abc15381fe4efd97c696e5c1b53082b8161309c4ee2", + "https://deno.land/x/postgresjs@v3.4.0/src/queue.js": "15e6345adb6708bf3b99ad39fc2231c2fb61de5f6cba4b7a7a6be881482a4ec3", + "https://deno.land/x/postgresjs@v3.4.0/src/result.js": "001ff5e0c8d634674f483d07fbcd620a797e3101f842d6c20ca3ace936260465", + "https://deno.land/x/postgresjs@v3.4.0/src/subscribe.js": "6ac0679e186ab4e681c55252ff50ac4e567df47a7d1382e6e37928f3e9f1fff8", + "https://deno.land/x/postgresjs@v3.4.0/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed", + "https://deno.land/x/postgresjs@v3.4.2/mod.js": "cb68f17d6d90df318934deccdb469d740be0888e7a597a9e7eea7100ce36a252", + "https://deno.land/x/postgresjs@v3.4.2/polyfills.js": "318eb01f2b4cc33a46c59f3ddc11f22a56d6b1db8b7719b2ad7decee63a5bd47", + "https://deno.land/x/postgresjs@v3.4.2/src/bytes.js": "f2de43bdc8fa5dc4b169f2c70d5d8b053a3dea8f85ef011d7b27dec69e14ebb7", + "https://deno.land/x/postgresjs@v3.4.2/src/connection.js": "4e43c34284c63c1895becdca54a900261254d9761e999888698ef989e9399132", + "https://deno.land/x/postgresjs@v3.4.2/src/errors.js": "85cfbed9a5ab0db41ab8e97b806c881af29807dfe99bc656fdf1a18c1c13b6c6", + "https://deno.land/x/postgresjs@v3.4.2/src/index.js": "9dca008e765675f8218d4e2e3ccc75359cc2240f7be4e80bf6735e92b5562e3a", + "https://deno.land/x/postgresjs@v3.4.2/src/large.js": "f3e770cdb7cc695f7b50687b4c6c4b7252129515486ec8def98b7582ee7c54ef", + "https://deno.land/x/postgresjs@v3.4.2/src/query.js": "67c45a5151032aa46b587abc15381fe4efd97c696e5c1b53082b8161309c4ee2", + "https://deno.land/x/postgresjs@v3.4.2/src/queue.js": "15e6345adb6708bf3b99ad39fc2231c2fb61de5f6cba4b7a7a6be881482a4ec3", + "https://deno.land/x/postgresjs@v3.4.2/src/result.js": "001ff5e0c8d634674f483d07fbcd620a797e3101f842d6c20ca3ace936260465", + "https://deno.land/x/postgresjs@v3.4.2/src/subscribe.js": "6ac0679e186ab4e681c55252ff50ac4e567df47a7d1382e6e37928f3e9f1fff8", + "https://deno.land/x/postgresjs@v3.4.2/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed", + "https://deno.land/x/postgresjs@v3.4.3/mod.js": "cb68f17d6d90df318934deccdb469d740be0888e7a597a9e7eea7100ce36a252", + "https://deno.land/x/postgresjs@v3.4.3/polyfills.js": "318eb01f2b4cc33a46c59f3ddc11f22a56d6b1db8b7719b2ad7decee63a5bd47", + "https://deno.land/x/postgresjs@v3.4.3/src/bytes.js": "f2de43bdc8fa5dc4b169f2c70d5d8b053a3dea8f85ef011d7b27dec69e14ebb7", + "https://deno.land/x/postgresjs@v3.4.3/src/connection.js": "506df20bbaa44256e8511ee87e5f18e6343c406f7b9804c20dd182c1b5b4270b", + "https://deno.land/x/postgresjs@v3.4.3/src/errors.js": "85cfbed9a5ab0db41ab8e97b806c881af29807dfe99bc656fdf1a18c1c13b6c6", + "https://deno.land/x/postgresjs@v3.4.3/src/index.js": "9dca008e765675f8218d4e2e3ccc75359cc2240f7be4e80bf6735e92b5562e3a", + "https://deno.land/x/postgresjs@v3.4.3/src/large.js": "f3e770cdb7cc695f7b50687b4c6c4b7252129515486ec8def98b7582ee7c54ef", + "https://deno.land/x/postgresjs@v3.4.3/src/query.js": "67c45a5151032aa46b587abc15381fe4efd97c696e5c1b53082b8161309c4ee2", + "https://deno.land/x/postgresjs@v3.4.3/src/queue.js": "15e6345adb6708bf3b99ad39fc2231c2fb61de5f6cba4b7a7a6be881482a4ec3", + "https://deno.land/x/postgresjs@v3.4.3/src/result.js": "001ff5e0c8d634674f483d07fbcd620a797e3101f842d6c20ca3ace936260465", + "https://deno.land/x/postgresjs@v3.4.3/src/subscribe.js": "6ac0679e186ab4e681c55252ff50ac4e567df47a7d1382e6e37928f3e9f1fff8", + "https://deno.land/x/postgresjs@v3.4.3/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed", + "https://deno.land/x/postgresjs@v3.4.4/mod.js": "cb68f17d6d90df318934deccdb469d740be0888e7a597a9e7eea7100ce36a252", + "https://deno.land/x/postgresjs@v3.4.4/polyfills.js": "318eb01f2b4cc33a46c59f3ddc11f22a56d6b1db8b7719b2ad7decee63a5bd47", + "https://deno.land/x/postgresjs@v3.4.4/src/bytes.js": "f2de43bdc8fa5dc4b169f2c70d5d8b053a3dea8f85ef011d7b27dec69e14ebb7", + "https://deno.land/x/postgresjs@v3.4.4/src/connection.js": "c63d53a0f35a7eb2670befef551f23fe914bbe9f0590de974e3e210c50527a29", + "https://deno.land/x/postgresjs@v3.4.4/src/errors.js": "85cfbed9a5ab0db41ab8e97b806c881af29807dfe99bc656fdf1a18c1c13b6c6", + "https://deno.land/x/postgresjs@v3.4.4/src/index.js": "9dca008e765675f8218d4e2e3ccc75359cc2240f7be4e80bf6735e92b5562e3a", + "https://deno.land/x/postgresjs@v3.4.4/src/large.js": "f3e770cdb7cc695f7b50687b4c6c4b7252129515486ec8def98b7582ee7c54ef", + "https://deno.land/x/postgresjs@v3.4.4/src/query.js": "67c45a5151032aa46b587abc15381fe4efd97c696e5c1b53082b8161309c4ee2", + "https://deno.land/x/postgresjs@v3.4.4/src/queue.js": "15e6345adb6708bf3b99ad39fc2231c2fb61de5f6cba4b7a7a6be881482a4ec3", + "https://deno.land/x/postgresjs@v3.4.4/src/result.js": "001ff5e0c8d634674f483d07fbcd620a797e3101f842d6c20ca3ace936260465", + "https://deno.land/x/postgresjs@v3.4.4/src/subscribe.js": "9e4d0c3e573a6048e77ee2f15abbd5bcd17da9ca85a78c914553472c6d6c169b", + "https://deno.land/x/postgresjs@v3.4.4/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed", + "https://deno.land/x/postgresjs@v3.4.5/mod.js": "cb68f17d6d90df318934deccdb469d740be0888e7a597a9e7eea7100ce36a252", + "https://deno.land/x/postgresjs@v3.4.5/polyfills.js": "318eb01f2b4cc33a46c59f3ddc11f22a56d6b1db8b7719b2ad7decee63a5bd47", + "https://deno.land/x/postgresjs@v3.4.5/src/bytes.js": "f2de43bdc8fa5dc4b169f2c70d5d8b053a3dea8f85ef011d7b27dec69e14ebb7", + "https://deno.land/x/postgresjs@v3.4.5/src/connection.js": "e63062451fb6a7284c14540b3f268d1373c3028fb0f3b234056ad56569190e8f", + "https://deno.land/x/postgresjs@v3.4.5/src/errors.js": "85cfbed9a5ab0db41ab8e97b806c881af29807dfe99bc656fdf1a18c1c13b6c6", + "https://deno.land/x/postgresjs@v3.4.5/src/index.js": "9dca008e765675f8218d4e2e3ccc75359cc2240f7be4e80bf6735e92b5562e3a", + "https://deno.land/x/postgresjs@v3.4.5/src/large.js": "f3e770cdb7cc695f7b50687b4c6c4b7252129515486ec8def98b7582ee7c54ef", + "https://deno.land/x/postgresjs@v3.4.5/src/query.js": "67c45a5151032aa46b587abc15381fe4efd97c696e5c1b53082b8161309c4ee2", + "https://deno.land/x/postgresjs@v3.4.5/src/queue.js": "15e6345adb6708bf3b99ad39fc2231c2fb61de5f6cba4b7a7a6be881482a4ec3", + "https://deno.land/x/postgresjs@v3.4.5/src/result.js": "001ff5e0c8d634674f483d07fbcd620a797e3101f842d6c20ca3ace936260465", + "https://deno.land/x/postgresjs@v3.4.5/src/subscribe.js": "9e4d0c3e573a6048e77ee2f15abbd5bcd17da9ca85a78c914553472c6d6c169b", + "https://deno.land/x/postgresjs@v3.4.5/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed", + "https://deno.land/x/postgresjs@v3.4.6/mod.js": "cb68f17d6d90df318934deccdb469d740be0888e7a597a9e7eea7100ce36a252", + "https://deno.land/x/postgresjs@v3.4.6/polyfills.js": "318eb01f2b4cc33a46c59f3ddc11f22a56d6b1db8b7719b2ad7decee63a5bd47", + "https://deno.land/x/postgresjs@v3.4.6/src/bytes.js": "f2de43bdc8fa5dc4b169f2c70d5d8b053a3dea8f85ef011d7b27dec69e14ebb7", + "https://deno.land/x/postgresjs@v3.4.6/src/connection.js": "3e93a3637a113ec558de83f65840ebfb50fe18e7e3e564df93b09a320df44a1f", + "https://deno.land/x/postgresjs@v3.4.6/src/errors.js": "85cfbed9a5ab0db41ab8e97b806c881af29807dfe99bc656fdf1a18c1c13b6c6", + "https://deno.land/x/postgresjs@v3.4.6/src/index.js": "ef6bbdf3778c7b5c1b539da4ce2df67640ffb3b930dccfc7e2e2dde559333794", + "https://deno.land/x/postgresjs@v3.4.6/src/large.js": "f3e770cdb7cc695f7b50687b4c6c4b7252129515486ec8def98b7582ee7c54ef", + "https://deno.land/x/postgresjs@v3.4.6/src/query.js": "67c45a5151032aa46b587abc15381fe4efd97c696e5c1b53082b8161309c4ee2", + "https://deno.land/x/postgresjs@v3.4.6/src/queue.js": "15e6345adb6708bf3b99ad39fc2231c2fb61de5f6cba4b7a7a6be881482a4ec3", + "https://deno.land/x/postgresjs@v3.4.6/src/result.js": "001ff5e0c8d634674f483d07fbcd620a797e3101f842d6c20ca3ace936260465", + "https://deno.land/x/postgresjs@v3.4.6/src/subscribe.js": "9e4d0c3e573a6048e77ee2f15abbd5bcd17da9ca85a78c914553472c6d6c169b", + "https://deno.land/x/postgresjs@v3.4.6/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed", "https://deno.land/x/postgresjs@v3.4.7/mod.js": "cb68f17d6d90df318934deccdb469d740be0888e7a597a9e7eea7100ce36a252", "https://deno.land/x/postgresjs@v3.4.7/polyfills.js": "318eb01f2b4cc33a46c59f3ddc11f22a56d6b1db8b7719b2ad7decee63a5bd47", "https://deno.land/x/postgresjs@v3.4.7/src/bytes.js": "f2de43bdc8fa5dc4b169f2c70d5d8b053a3dea8f85ef011d7b27dec69e14ebb7",