From d73370cc68f3bdace2d93417b80cbc43e3182a8b Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 31 Jul 2024 12:52:03 -0500 Subject: [PATCH] Convert IN operators to = ANY() operators on Postgres --- deno.json | 2 +- deno.lock | 7 ++++- src/db/adapters/DittoPostgres.ts | 48 ++++++++++++++++++++++++++++---- src/db/adapters/DittoSQLite.ts | 1 + src/storages/EventsDB.ts | 1 + 5 files changed, 52 insertions(+), 7 deletions(-) diff --git a/deno.json b/deno.json index 1b4fd7ae..0867a999 100644 --- a/deno.json +++ b/deno.json @@ -50,7 +50,7 @@ "hono-rate-limiter": "npm:hono-rate-limiter@^0.3.0", "iso-639-1": "npm:iso-639-1@2.1.15", "isomorphic-dompurify": "npm:isomorphic-dompurify@^2.11.0", - "kysely": "npm:kysely@^0.27.3", + "kysely": "npm:kysely@^0.27.4", "postgres": "https://raw.githubusercontent.com/xyzshantaram/postgres.js/8a9bbce88b3f6425ecaacd99a80372338b157a53/deno/mod.js", "kysely-postgres-js": "npm:kysely-postgres-js@2.0.0", "light-bolt11-decoder": "npm:light-bolt11-decoder", diff --git a/deno.lock b/deno.lock index 7b32e82b..d08819de 100644 --- a/deno.lock +++ b/deno.lock @@ -53,6 +53,7 @@ "npm:kysely@0.27.3": "npm:kysely@0.27.3", "npm:kysely@^0.27.2": "npm:kysely@0.27.3", "npm:kysely@^0.27.3": "npm:kysely@0.27.3", + "npm:kysely@^0.27.4": "npm:kysely@0.27.4", "npm:light-bolt11-decoder": "npm:light-bolt11-decoder@3.1.1", "npm:linkify-plugin-hashtag@^4.1.1": "npm:linkify-plugin-hashtag@4.1.3_linkifyjs@4.1.3", "npm:linkify-string@^4.1.1": "npm:linkify-string@4.1.3_linkifyjs@4.1.3", @@ -710,6 +711,10 @@ "integrity": "sha512-lG03Ru+XyOJFsjH3OMY6R/9U38IjDPfnOfDgO3ynhbDr+Dz8fak+X6L62vqu3iybQnj+lG84OttBuU9KY3L9kA==", "dependencies": {} }, + "kysely@0.27.4": { + "integrity": "sha512-dyNKv2KRvYOQPLCAOCjjQuCk4YFd33BvGdf/o5bC7FiW+BB6snA81Zt+2wT9QDFzKqxKa5rrOmvlK/anehCcgA==", + "dependencies": {} + }, "light-bolt11-decoder@3.1.1": { "integrity": "sha512-sLg/KCwYkgsHWkefWd6KqpCHrLFWWaXTOX3cf6yD2hAzL0SLpX+lFcaFK2spkjbgzG6hhijKfORDc9WoUHwX0A==", "dependencies": { @@ -1799,7 +1804,7 @@ "npm:iso-639-1@2.1.15", "npm:isomorphic-dompurify@^2.11.0", "npm:kysely-postgres-js@2.0.0", - "npm:kysely@^0.27.3", + "npm:kysely@^0.27.4", "npm:light-bolt11-decoder", "npm:linkify-plugin-hashtag@^4.1.1", "npm:linkify-string@^4.1.1", diff --git a/src/db/adapters/DittoPostgres.ts b/src/db/adapters/DittoPostgres.ts index 3be08d47..e6793a86 100644 --- a/src/db/adapters/DittoPostgres.ts +++ b/src/db/adapters/DittoPostgres.ts @@ -1,5 +1,15 @@ -import { Kysely } from 'kysely'; -import { PostgresJSDialect, PostgresJSDialectConfig } from 'kysely-postgres-js'; +import { + BinaryOperationNode, + FunctionNode, + Kysely, + OperatorNode, + PostgresAdapter, + PostgresIntrospector, + PostgresQueryCompiler, + PrimitiveValueListNode, + ValueNode, +} from 'kysely'; +import { PostgresJSDialectConfig, PostgresJSDriver } from 'kysely-postgres-js'; import postgres from 'postgres'; import { Conf } from '@/config.ts'; @@ -18,9 +28,22 @@ export class DittoPostgres { if (!this.db) { this.db = new Kysely({ - dialect: new PostgresJSDialect({ - postgres: this.postgres as unknown as PostgresJSDialectConfig['postgres'], - }), + dialect: { + createAdapter() { + return new PostgresAdapter(); + }, + createDriver() { + return new PostgresJSDriver({ + postgres: DittoPostgres.postgres as unknown as PostgresJSDialectConfig['postgres'], + }); + }, + createIntrospector(db) { + return new PostgresIntrospector(db); + }, + createQueryCompiler() { + return new DittoPostgresQueryCompiler(); + }, + }, log: KyselyLogger, }); } @@ -36,3 +59,18 @@ export class DittoPostgres { return this.postgres?.connections.idle ?? 0; } } + +/** Converts `in` queries to `any` to improve prepared statements on Postgres. */ +class DittoPostgresQueryCompiler extends PostgresQueryCompiler { + protected override visitBinaryOperation(node: BinaryOperationNode): void { + if ( + OperatorNode.is(node.operator) && node.operator.operator === 'in' && PrimitiveValueListNode.is(node.rightOperand) + ) { + this.visitNode(node.leftOperand); + this.append(' = '); + this.visitNode(FunctionNode.create('any', [ValueNode.create(node.rightOperand.values)])); + } else { + super.visitBinaryOperation(node); + } + } +} diff --git a/src/db/adapters/DittoSQLite.ts b/src/db/adapters/DittoSQLite.ts index d412ca31..3574f4b8 100644 --- a/src/db/adapters/DittoSQLite.ts +++ b/src/db/adapters/DittoSQLite.ts @@ -15,6 +15,7 @@ export class DittoSQLite { await sqliteWorker.open(this.path); this.db = new Kysely({ + // @ts-ignore Kysely version mismatch. dialect: new PolySqliteDialect({ database: sqliteWorker, }), diff --git a/src/storages/EventsDB.ts b/src/storages/EventsDB.ts index abf076c7..c9525c10 100644 --- a/src/storages/EventsDB.ts +++ b/src/storages/EventsDB.ts @@ -43,6 +43,7 @@ class EventsDB implements NStore { }; constructor(private kysely: Kysely) { + // @ts-ignore Kysely version mismatch. this.store = new NDatabase(kysely, { fts: Conf.db.dialect, timeoutStrategy: Conf.db.dialect === 'postgres' ? 'setStatementTimeout' : undefined,