@rocicorp/zero 1.11.0-canary.5 → 1.11.0-canary.7
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/out/analyze-query/src/analyze-cli.js +2 -2
- package/out/analyze-query/src/analyze-cli.js.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.d.ts.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.js +27 -4
- package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/sqlite-store.d.ts +11 -0
- package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
- package/out/replicache/src/kv/sqlite-store.js +43 -3
- package/out/replicache/src/kv/sqlite-store.js.map +1 -1
- package/out/shared/src/error.d.ts +11 -0
- package/out/shared/src/error.d.ts.map +1 -1
- package/out/shared/src/error.js +35 -1
- package/out/shared/src/error.js.map +1 -1
- package/out/shared/src/map.d.ts +7 -2
- package/out/shared/src/map.d.ts.map +1 -1
- package/out/shared/src/map.js.map +1 -1
- package/out/zero/package.js +2 -2
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/config/normalize.d.ts.map +1 -1
- package/out/zero-cache/src/config/normalize.js +7 -1
- package/out/zero-cache/src/config/normalize.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +9 -2
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +26 -7
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +15 -6
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/main.d.ts.map +1 -1
- package/out/zero-cache/src/server/main.js +8 -1
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/mutator.js +2 -0
- package/out/zero-cache/src/server/mutator.js.map +1 -1
- package/out/zero-cache/src/server/reaper.d.ts.map +1 -1
- package/out/zero-cache/src/server/reaper.js +2 -0
- package/out/zero-cache/src/server/reaper.js.map +1 -1
- package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/server/replicator.js +2 -0
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/server/runner/zero-dispatcher.d.ts.map +1 -1
- package/out/zero-cache/src/server/runner/zero-dispatcher.js +3 -0
- package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +2 -0
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js +14 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js +15 -3
- package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +2 -2
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +68 -8
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js +58 -7
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts +19 -4
- package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-log-codec.js +28 -8
- package/out/zero-cache/src/services/change-streamer/change-log-codec.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +3 -0
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +6 -4
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +6 -0
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +86 -51
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.js +4 -4
- package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot.d.ts +3 -1
- package/out/zero-cache/src/services/change-streamer/snapshot.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot.js +54 -20
- package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts +6 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +27 -9
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.d.ts +2 -2
- package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js +8 -5
- package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
- package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +51 -24
- package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts +5 -0
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js +7 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js +2 -1
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/profz.d.ts +7 -0
- package/out/zero-cache/src/services/profz.d.ts.map +1 -0
- package/out/zero-cache/src/services/profz.js +105 -0
- package/out/zero-cache/src/services/profz.js.map +1 -0
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts +5 -4
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +20 -22
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts +6 -6
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +19 -14
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -1
- package/out/zero-cache/src/services/runner.d.ts.map +1 -1
- package/out/zero-cache/src/services/runner.js +1 -1
- package/out/zero-cache/src/services/runner.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts +17 -0
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +59 -0
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts +51 -0
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js +78 -0
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +18 -6
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +174 -6
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/processes.d.ts +32 -0
- package/out/zero-cache/src/types/processes.d.ts.map +1 -1
- package/out/zero-cache/src/types/processes.js +43 -4
- package/out/zero-cache/src/types/processes.js.map +1 -1
- package/out/zero-cache/src/types/profiler.d.ts +12 -0
- package/out/zero-cache/src/types/profiler.d.ts.map +1 -1
- package/out/zero-cache/src/types/profiler.js +46 -1
- package/out/zero-cache/src/types/profiler.js.map +1 -1
- package/out/zero-cache/src/workers/connection.js +3 -3
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js +21 -16
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +10 -1
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/connection-manager.d.ts.map +1 -1
- package/out/zero-client/src/client/connection-manager.js +46 -0
- package/out/zero-client/src/client/connection-manager.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +12 -8
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-protocol/src/down.d.ts +27 -27
- package/out/zero-react/src/use-query.d.ts.map +1 -1
- package/out/zero-react/src/use-query.js +50 -3
- package/out/zero-react/src/use-query.js.map +1 -1
- package/out/zero-solid/src/use-query.js +1 -2
- package/out/zero-solid/src/use-query.js.map +1 -1
- package/out/zql/src/ivm/data.d.ts.map +1 -1
- package/out/zql/src/ivm/data.js +8 -3
- package/out/zql/src/ivm/data.js.map +1 -1
- package/out/zql/src/ivm/view-apply-change.js +6 -1
- package/out/zql/src/ivm/view-apply-change.js.map +1 -1
- package/out/zqlite/src/db.d.ts +9 -1
- package/out/zqlite/src/db.d.ts.map +1 -1
- package/out/zqlite/src/db.js +8 -2
- package/out/zqlite/src/db.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assert } from "../../../../../shared/src/asserts.js";
|
|
2
|
+
import { AbortError } from "../../../../../shared/src/abort-error.js";
|
|
2
3
|
import { Subscription } from "../../../types/subscription.js";
|
|
3
4
|
import { resolver } from "@rocicorp/resolver";
|
|
4
5
|
//#region ../zero-cache/src/services/change-source/common/change-stream-multiplexer.ts
|
|
@@ -22,11 +23,20 @@ var ChangeStreamMultiplexer = class {
|
|
|
22
23
|
* Tracks the queue of producers waiting for a reservation.
|
|
23
24
|
*/
|
|
24
25
|
#waiters = [];
|
|
26
|
+
#canceled = false;
|
|
25
27
|
constructor(lc, lastWatermark) {
|
|
26
28
|
this.#lc = lc;
|
|
27
|
-
this.#sub = Subscription.create({ cleanup: () => this.#
|
|
29
|
+
this.#sub = Subscription.create({ cleanup: () => this.#cancel() });
|
|
28
30
|
this.#lastWatermark = lastWatermark;
|
|
29
31
|
}
|
|
32
|
+
#cancel() {
|
|
33
|
+
this.#canceled = true;
|
|
34
|
+
this.#producers.forEach((p) => p.cancel());
|
|
35
|
+
for (const { producer, rejectReservation } of this.#waiters.splice(0)) {
|
|
36
|
+
this.#lc.info?.(`rejecting reservation request from ${producer}: stream canceled`);
|
|
37
|
+
rejectReservation(new AbortError("change stream canceled"));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
30
40
|
addProducers(...p) {
|
|
31
41
|
this.#producers.push(...p);
|
|
32
42
|
return this;
|
|
@@ -46,17 +56,19 @@ var ChangeStreamMultiplexer = class {
|
|
|
46
56
|
* @param producer The name of the producer, purely for debugging output
|
|
47
57
|
*/
|
|
48
58
|
reserve(producer) {
|
|
59
|
+
if (this.#canceled) return Promise.reject(new AbortError("change stream canceled"));
|
|
49
60
|
if (this.#lastWatermark !== null) {
|
|
50
61
|
const lastWatermark = this.#lastWatermark;
|
|
51
62
|
this.#lastWatermark = null;
|
|
52
63
|
return lastWatermark;
|
|
53
64
|
}
|
|
54
65
|
const startTime = performance.now();
|
|
55
|
-
const { promise, resolve: grantReservation } = resolver();
|
|
66
|
+
const { promise, resolve: grantReservation, reject: rejectReservation } = resolver();
|
|
56
67
|
this.#waiters.push({
|
|
57
68
|
producer,
|
|
58
69
|
startTime,
|
|
59
|
-
grantReservation
|
|
70
|
+
grantReservation,
|
|
71
|
+
rejectReservation
|
|
60
72
|
});
|
|
61
73
|
return promise;
|
|
62
74
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-stream-multiplexer.js","names":["#lc","#sub","#producers","#listeners","#waiters","#lastWatermark"],"sources":["../../../../../../../zero-cache/src/services/change-source/common/change-stream-multiplexer.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {assert} from '../../../../../shared/src/asserts.ts';\nimport type {Source} from '../../../types/streams.ts';\nimport {Subscription} from '../../../types/subscription.ts';\nimport type {\n ChangeStreamMessage,\n DownstreamStatusMessage,\n} from '../protocol/current.ts';\n\nexport type Cancelable = {\n cancel(): void;\n};\n\nexport type Listener = {\n onChange(change: ChangeStreamMessage): void;\n};\n\ntype Waiter = {\n producer: string;\n startTime: number;\n grantReservation: (watermark: string) => void;\n};\n\n/**\n * Facilitates cooperative multiplexing of transactions from\n * multiple producers into a single change stream.\n */\nexport class ChangeStreamMultiplexer {\n readonly #lc: LogContext;\n readonly #sub: Subscription<ChangeStreamMessage>;\n readonly #producers: Cancelable[] = [];\n readonly #listeners: Listener[] = [];\n\n /**\n * The `#lastWatermark` tracks the watermark of the last transaction\n * committed to stream. It is set to null when a producer has reserved\n * the stream, and set to the new watermark when the producer releases\n * the reservation.\n */\n #lastWatermark: string | null;\n\n /**\n * Tracks the queue of producers waiting for a reservation.\n */\n readonly #waiters: Waiter[] = [];\n\n constructor(lc: LogContext, lastWatermark: string) {\n this.#lc = lc;\n this.#sub = Subscription.create<ChangeStreamMessage>({\n cleanup: () => this.#producers.forEach(p => p.cancel())
|
|
1
|
+
{"version":3,"file":"change-stream-multiplexer.js","names":["#lc","#sub","#producers","#listeners","#waiters","#cancel","#lastWatermark","#canceled"],"sources":["../../../../../../../zero-cache/src/services/change-source/common/change-stream-multiplexer.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {AbortError} from '../../../../../shared/src/abort-error.ts';\nimport {assert} from '../../../../../shared/src/asserts.ts';\nimport type {Source} from '../../../types/streams.ts';\nimport {Subscription} from '../../../types/subscription.ts';\nimport type {\n ChangeStreamMessage,\n DownstreamStatusMessage,\n} from '../protocol/current.ts';\n\nexport type Cancelable = {\n cancel(): void;\n};\n\nexport type Listener = {\n onChange(change: ChangeStreamMessage): void;\n};\n\ntype Waiter = {\n producer: string;\n startTime: number;\n grantReservation: (watermark: string) => void;\n rejectReservation: (err: Error) => void;\n};\n\n/**\n * Facilitates cooperative multiplexing of transactions from\n * multiple producers into a single change stream.\n */\nexport class ChangeStreamMultiplexer {\n readonly #lc: LogContext;\n readonly #sub: Subscription<ChangeStreamMessage>;\n readonly #producers: Cancelable[] = [];\n readonly #listeners: Listener[] = [];\n\n /**\n * The `#lastWatermark` tracks the watermark of the last transaction\n * committed to stream. It is set to null when a producer has reserved\n * the stream, and set to the new watermark when the producer releases\n * the reservation.\n */\n #lastWatermark: string | null;\n\n /**\n * Tracks the queue of producers waiting for a reservation.\n */\n readonly #waiters: Waiter[] = [];\n\n #canceled = false;\n\n constructor(lc: LogContext, lastWatermark: string) {\n this.#lc = lc;\n this.#sub = Subscription.create<ChangeStreamMessage>({\n cleanup: () => this.#cancel(),\n });\n this.#lastWatermark = lastWatermark;\n }\n\n #cancel() {\n this.#canceled = true;\n this.#producers.forEach(p => p.cancel());\n\n // A producer awaiting a reservation would otherwise remain suspended\n // forever, since the reservation held when the stream was canceled is\n // never released. Reject the pending reservations so that the producers\n // unwind and release the resources (e.g. upstream connections and\n // transactions) that they were holding for the stream.\n for (const {producer, rejectReservation} of this.#waiters.splice(0)) {\n this.#lc.info?.(\n `rejecting reservation request from ${producer}: stream canceled`,\n );\n rejectReservation(new AbortError('change stream canceled'));\n }\n }\n\n addProducers(...p: Cancelable[]): this {\n this.#producers.push(...p);\n return this;\n }\n\n addListeners(...l: Listener[]): this {\n this.#listeners.push(...l);\n return this;\n }\n\n /**\n * Called by a producer to \"reserve\" the exclusive right to push data\n * changes to the stream. If the stream is currently reserved,\n * the resulting Promise must be awaited.\n *\n * The producer must then {@link release()} the reservation when it sees\n * fit.\n *\n * @param producer The name of the producer, purely for debugging output\n */\n reserve(producer: string): string | Promise<string> {\n if (this.#canceled) {\n return Promise.reject(new AbortError('change stream canceled'));\n }\n if (this.#lastWatermark !== null) {\n // If the stream is not reserved, reserve it and return the\n // watermark.\n const lastWatermark = this.#lastWatermark;\n this.#lastWatermark = null;\n return lastWatermark;\n }\n\n // Otherwise, wait for the current reservation to be released.\n const startTime = performance.now();\n const {\n promise,\n resolve: grantReservation,\n reject: rejectReservation,\n } = resolver<string>();\n this.#waiters.push({\n producer,\n startTime,\n grantReservation,\n rejectReservation,\n });\n\n return promise;\n }\n\n /**\n * If there are producers currently awaiting a reservation, returns\n * the duration (in milliseconds) of the oldest reservation request.\n * Returns a negative number if there are no waiters.\n */\n waiterDelay(): number {\n if (this.#waiters.length === 0) {\n return -1;\n }\n return performance.now() - this.#waiters[0].startTime;\n }\n\n /**\n * Called by a producer to release its reservation after committing its\n * last transaction.\n */\n release(newWatermark: string) {\n const waiter = this.#waiters.shift();\n if (!waiter) {\n this.#lastWatermark = newWatermark;\n } else {\n const {producer, startTime, grantReservation} = waiter;\n grantReservation(newWatermark);\n const elapsed = performance.now() - startTime;\n this.#lc.info?.(\n `${producer} waited ${elapsed.toFixed(3)} ms for stream reservation`,\n );\n }\n }\n\n /**\n * `pushStatus()` can be called without a reservation, as it\n * does not constitute a data change and can appear anywhere\n * in the stream.\n */\n pushStatus(message: DownstreamStatusMessage) {\n // Let listeners know about all status messages.\n this.#listeners.forEach(l => l.onChange(message));\n // The ChangeStreamer only cares about status messages requiring an ack\n // or containing a lagReport. To reduce churn, avoid sending other status\n // messages.\n if (message[1].ack || message[1].lagReport) {\n this.#sub.push(message);\n }\n }\n\n /**\n * `push()` must only be called by a producer after it has\n * {@link reserve}d the stream.\n */\n push(message: ChangeStreamMessage): Promise<unknown> {\n assert(\n this.#lastWatermark === null,\n `push() called without reserve()-ing the stream`,\n );\n this.#listeners.forEach(l => l.onChange(message));\n return this.#sub.push(message).result;\n }\n\n fail(err: Error) {\n this.#sub.fail(err);\n }\n\n asSource(): Source<ChangeStreamMessage> {\n return this.#sub;\n }\n}\n"],"mappings":";;;;;;;;;AA8BA,IAAa,0BAAb,MAAqC;CACnC;CACA;CACA,aAAoC,CAAC;CACrC,aAAkC,CAAC;;;;;;;CAQnC;;;;CAKA,WAA8B,CAAC;CAE/B,YAAY;CAEZ,YAAY,IAAgB,eAAuB;EACjD,KAAKA,MAAM;EACX,KAAKC,OAAO,aAAa,OAA4B,EACnD,eAAe,KAAKI,QAAQ,EAC9B,CAAC;EACD,KAAKC,iBAAiB;CACxB;CAEA,UAAU;EACR,KAAKC,YAAY;EACjB,KAAKL,WAAW,SAAQ,MAAK,EAAE,OAAO,CAAC;EAOvC,KAAK,MAAM,EAAC,UAAU,uBAAsB,KAAKE,SAAS,OAAO,CAAC,GAAG;GACnE,KAAKJ,IAAI,OACP,sCAAsC,SAAS,kBACjD;GACA,kBAAkB,IAAI,WAAW,wBAAwB,CAAC;EAC5D;CACF;CAEA,aAAa,GAAG,GAAuB;EACrC,KAAKE,WAAW,KAAK,GAAG,CAAC;EACzB,OAAO;CACT;CAEA,aAAa,GAAG,GAAqB;EACnC,KAAKC,WAAW,KAAK,GAAG,CAAC;EACzB,OAAO;CACT;;;;;;;;;;;CAYA,QAAQ,UAA4C;EAClD,IAAI,KAAKI,WACP,OAAO,QAAQ,OAAO,IAAI,WAAW,wBAAwB,CAAC;EAEhE,IAAI,KAAKD,mBAAmB,MAAM;GAGhC,MAAM,gBAAgB,KAAKA;GAC3B,KAAKA,iBAAiB;GACtB,OAAO;EACT;EAGA,MAAM,YAAY,YAAY,IAAI;EAClC,MAAM,EACJ,SACA,SAAS,kBACT,QAAQ,sBACN,SAAiB;EACrB,KAAKF,SAAS,KAAK;GACjB;GACA;GACA;GACA;EACF,CAAC;EAED,OAAO;CACT;;;;;;CAOA,cAAsB;EACpB,IAAI,KAAKA,SAAS,WAAW,GAC3B,OAAO;EAET,OAAO,YAAY,IAAI,IAAI,KAAKA,SAAS,GAAG;CAC9C;;;;;CAMA,QAAQ,cAAsB;EAC5B,MAAM,SAAS,KAAKA,SAAS,MAAM;EACnC,IAAI,CAAC,QACH,KAAKE,iBAAiB;OACjB;GACL,MAAM,EAAC,UAAU,WAAW,qBAAoB;GAChD,iBAAiB,YAAY;GAC7B,MAAM,UAAU,YAAY,IAAI,IAAI;GACpC,KAAKN,IAAI,OACP,GAAG,SAAS,UAAU,QAAQ,QAAQ,CAAC,EAAE,2BAC3C;EACF;CACF;;;;;;CAOA,WAAW,SAAkC;EAE3C,KAAKG,WAAW,SAAQ,MAAK,EAAE,SAAS,OAAO,CAAC;EAIhD,IAAI,QAAQ,GAAG,OAAO,QAAQ,GAAG,WAC/B,KAAKF,KAAK,KAAK,OAAO;CAE1B;;;;;CAMA,KAAK,SAAgD;EACnD,OACE,KAAKK,mBAAmB,MACxB,gDACF;EACA,KAAKH,WAAW,SAAQ,MAAK,EAAE,SAAS,OAAO,CAAC;EAChD,OAAO,KAAKF,KAAK,KAAK,OAAO,EAAE;CACjC;CAEA,KAAK,KAAY;EACf,KAAKA,KAAK,KAAK,GAAG;CACpB;CAEA,WAAwC;EACtC,OAAO,KAAKA;CACd;AACF"}
|
|
@@ -13,7 +13,7 @@ import { ChangeProcessor } from "../../replicator/change-processor.js";
|
|
|
13
13
|
import { ReplicationStatusPublisher } from "../../replicator/replication-status.js";
|
|
14
14
|
import { restoreReplica } from "../common/replica-restore.js";
|
|
15
15
|
import { initReplica } from "../common/replica-schema.js";
|
|
16
|
-
import { WebSocket
|
|
16
|
+
import { WebSocket } from "ws";
|
|
17
17
|
//#region ../zero-cache/src/services/change-source/custom/change-source.ts
|
|
18
18
|
/**
|
|
19
19
|
* Initializes a Custom change source before streaming changes from the
|
|
@@ -78,7 +78,7 @@ var CustomChangeSource = class {
|
|
|
78
78
|
url.searchParams.set("lastWatermark", clientWatermark);
|
|
79
79
|
url.searchParams.set("replicaVersion", replicaVersion);
|
|
80
80
|
}
|
|
81
|
-
const ws = new WebSocket
|
|
81
|
+
const ws = new WebSocket(url);
|
|
82
82
|
const { instream, outstream } = stream(this.#lc, ws, changeStreamMessageSchema, { coalesce: (curr) => curr });
|
|
83
83
|
return {
|
|
84
84
|
changes: instream,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-source.js","names":["#lc","#upstreamUri","#shard","#replicationConfig","#startStream"],"sources":["../../../../../../../zero-cache/src/services/change-source/custom/change-source.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {WebSocket} from 'ws';\nimport {assert, unreachable} from '../../../../../shared/src/asserts.ts';\nimport {\n stringify,\n type JSONObject,\n} from '../../../../../shared/src/bigint-json.ts';\nimport {deepEqual} from '../../../../../shared/src/json.ts';\nimport type {SchemaValue} from '../../../../../zero-schema/src/table-schema.ts';\nimport {Database} from '../../../../../zqlite/src/db.ts';\nimport {computeZqlSpecs} from '../../../db/lite-tables.ts';\nimport {StatementRunner} from '../../../db/statements.ts';\nimport type {ShardConfig, ShardID} from '../../../types/shards.ts';\nimport {stream} from '../../../types/streams.ts';\nimport {\n AutoResetSignal,\n type ReplicationConfig,\n} from '../../change-streamer/schema/tables.ts';\nimport {ChangeProcessor} from '../../replicator/change-processor.ts';\nimport {ReplicationStatusPublisher} from '../../replicator/replication-status.ts';\nimport {\n createReplicationStateTables,\n getSubscriptionState,\n initReplicationState,\n} from '../../replicator/schema/replication-state.ts';\nimport type {ChangeSource, ChangeStream} from '../change-source.ts';\nimport {\n restoreReplica,\n type InitializeResult,\n type RestoreOptions,\n} from '../common/replica-restore.ts';\nimport {initReplica} from '../common/replica-schema.ts';\nimport {changeStreamMessageSchema} from '../protocol/current/downstream.ts';\nimport {\n type BackfillRequest,\n type ChangeSourceUpstream,\n} from '../protocol/current/upstream.ts';\n\n/** Server context to store with the initial sync metadata for debugging. */\nexport type ServerContext = JSONObject;\n\n/**\n * Initializes a Custom change source before streaming changes from the\n * corresponding logical replication stream.\n */\nexport async function initializeCustomChangeSource(\n lc: LogContext,\n upstreamURI: string,\n shard: ShardConfig,\n replicaDbFile: string,\n context: ServerContext,\n {litestream, constraints}: RestoreOptions = {},\n): Promise<InitializeResult> {\n // At the moment, the custom change-source implementation does not support\n // per-RM backups (and thus does not support litestream v5 or RMv2). The\n // current proposal to support this without requiring additional upstream\n // state tracking is to:\n // - use litestream to (ltx) query a fixed pool of subfolders within the\n // backupURL (e.g. /a/, /b/, /c/, /d/, /e/)\n // - restore from the subfolder with the latest ltx file\n // - backup to the subfolder with the oldest (or non-existent) ltx file\n if (litestream?.backupURL) {\n await restoreReplica(lc, litestream, replicaDbFile, constraints);\n }\n\n let initialSynced = false;\n await initReplica(\n lc,\n `replica-${shard.appID}-${shard.shardNum}`,\n replicaDbFile,\n (log, tx) => {\n initialSynced = true;\n return initialSync(log, shard, tx, upstreamURI, context);\n },\n );\n\n const replica = new Database(lc, replicaDbFile);\n const subscriptionState = getSubscriptionState(new StatementRunner(replica));\n replica.close();\n\n if (shard.publications.length) {\n // Verify that the publications match what has been synced.\n const requested = shard.publications.toSorted();\n const replicated = subscriptionState.publications.sort();\n if (!deepEqual(requested, replicated)) {\n throw new Error(\n `Invalid ShardConfig. Requested publications [${requested}] do not match synced publications: [${replicated}]`,\n );\n }\n }\n\n const changeSource = new CustomChangeSource(\n lc,\n upstreamURI,\n shard,\n subscriptionState,\n );\n\n return {\n subscriptionState,\n changeSource,\n destinationBackupURL: litestream?.backupURL,\n // A custom change source has no upstream `replicas` table to identify\n // itself from, so the generation is the whole of the SQLite change log's\n // identity here.\n replicaID: null,\n waitForBackupBeforeServing: initialSynced,\n };\n}\n\nclass CustomChangeSource implements ChangeSource {\n readonly #lc: LogContext;\n readonly #upstreamUri: string;\n readonly #shard: ShardID;\n readonly #replicationConfig: ReplicationConfig;\n\n constructor(\n lc: LogContext,\n upstreamUri: string,\n shard: ShardID,\n replicationConfig: ReplicationConfig,\n ) {\n this.#lc = lc.withContext('component', 'change-source');\n this.#upstreamUri = upstreamUri;\n this.#shard = shard;\n this.#replicationConfig = replicationConfig;\n }\n\n initialSync(): ChangeStream {\n return this.#startStream();\n }\n\n startLagReporter() {\n return null; // Not supported for custom sources\n }\n\n stop(): Promise<void> {\n return Promise.resolve();\n }\n\n startStream(\n clientWatermark: string,\n backfillRequests: BackfillRequest[] = [],\n ): Promise<ChangeStream> {\n if (backfillRequests?.length) {\n throw new Error(\n 'backfill is yet not supported for custom change sources',\n );\n }\n return Promise.resolve(this.#startStream(clientWatermark));\n }\n\n #startStream(clientWatermark?: string): ChangeStream {\n const {publications, replicaVersion} = this.#replicationConfig;\n const {appID, shardNum} = this.#shard;\n const url = new URL(this.#upstreamUri);\n url.searchParams.set('appID', appID);\n url.searchParams.set('shardNum', String(shardNum));\n for (const pub of publications) {\n url.searchParams.append('publications', pub);\n }\n if (clientWatermark) {\n assert(\n replicaVersion.length,\n 'replicaVersion is required when clientWatermark is set',\n );\n url.searchParams.set('lastWatermark', clientWatermark);\n url.searchParams.set('replicaVersion', replicaVersion);\n }\n\n const ws = new WebSocket(url);\n const {instream, outstream} = stream(\n this.#lc,\n ws,\n changeStreamMessageSchema,\n // Upstream acks coalesce. If upstream exhibits back-pressure,\n // only the last ACK is kept / buffered.\n {coalesce: (curr: ChangeSourceUpstream) => curr},\n );\n return {changes: instream, acks: outstream};\n }\n}\n\n/**\n * Initial sync for a custom change source makes a request to the\n * change source endpoint with no `replicaVersion` or `lastWatermark`.\n * The initial transaction returned by the endpoint is treated as\n * the initial sync, and the commit watermark of that transaction\n * becomes the `replicaVersion` of the initialized replica.\n *\n * Note that this is equivalent to how the LSN of the Postgres WAL\n * at initial sync time is the `replicaVersion` (and starting\n * version for all initially-synced rows).\n */\nexport async function initialSync(\n lc: LogContext,\n shard: ShardConfig,\n tx: Database,\n upstreamURI: string,\n context: ServerContext,\n) {\n const {appID: id, publications} = shard;\n const changeSource = new CustomChangeSource(lc, upstreamURI, shard, {\n replicaVersion: '', // ignored for initialSync()\n publications,\n });\n const {changes} = changeSource.initialSync();\n\n createReplicationStateTables(tx);\n const processor = new ChangeProcessor(\n new StatementRunner(tx),\n 'initial-sync',\n (_, err) => {\n throw err;\n },\n );\n\n const statusPublisher = ReplicationStatusPublisher.forRunningTransaction(tx);\n try {\n let num = 0;\n for await (const change of changes) {\n const [tag] = change;\n switch (tag) {\n case 'begin': {\n const {commitWatermark} = change[2];\n lc.info?.(\n `initial sync of shard ${id} at replicaVersion ${commitWatermark}`,\n );\n statusPublisher.publish(\n lc,\n 'Initializing',\n `Copying upstream tables at version ${commitWatermark}`,\n 5000,\n );\n initReplicationState(\n tx,\n publications.toSorted(),\n commitWatermark,\n context,\n false,\n );\n processor.processMessage(lc, change);\n break;\n }\n case 'data':\n processor.processMessage(lc, change);\n if (++num % 1000 === 0) {\n lc.debug?.(`processed ${num} changes`);\n }\n break;\n case 'commit':\n processor.processMessage(lc, change);\n validateInitiallySyncedData(lc, tx, shard);\n lc.info?.(`finished initial-sync of ${num} changes`);\n return;\n\n case 'status':\n break; // Ignored\n // @ts-expect-error: falls through if the tag is not 'reset-required\n case 'control': {\n const {tag, message} = change[1];\n if (tag === 'reset-required') {\n throw new AutoResetSignal(\n message ?? 'auto-reset signaled by change source',\n );\n }\n }\n // falls through\n case 'rollback':\n throw new Error(\n `unexpected message during initial-sync: ${stringify(change)}`,\n );\n default:\n unreachable(change);\n }\n }\n throw new Error(\n `change source ${upstreamURI} closed before initial-sync completed`,\n );\n } catch (e) {\n await statusPublisher.publishAndThrowError(lc, 'Initializing', e);\n } finally {\n statusPublisher.stop();\n }\n}\n\n// Verify that the upstream tables expected by the sync logic\n// have been properly initialized.\nfunction getRequiredTables({\n appID,\n shardNum,\n}: ShardID): Record<string, Record<string, SchemaValue>> {\n return {\n [`${appID}_${shardNum}.clients`]: {\n clientGroupID: {type: 'string'},\n clientID: {type: 'string'},\n lastMutationID: {type: 'number'},\n userID: {type: 'string'},\n },\n [`${appID}_${shardNum}.mutations`]: {\n clientGroupID: {type: 'string'},\n clientID: {type: 'string'},\n mutationID: {type: 'number'},\n mutation: {type: 'json'},\n },\n [`${appID}.permissions`]: {\n permissions: {type: 'json'},\n hash: {type: 'string'},\n },\n };\n}\n\nfunction validateInitiallySyncedData(\n lc: LogContext,\n db: Database,\n shard: ShardID,\n) {\n const tables = computeZqlSpecs(lc, db, {includeBackfillingColumns: true});\n const required = getRequiredTables(shard);\n for (const [name, columns] of Object.entries(required)) {\n const table = tables.get(name)?.zqlSpec;\n if (!table) {\n throw new Error(\n `Upstream is missing the \"${name}\" table. (Found ${[\n ...tables.keys(),\n ]})` +\n `Please ensure that each table has a unique index over one ` +\n `or more non-null columns.`,\n );\n }\n for (const [col, {type}] of Object.entries(columns)) {\n const found = table[col];\n if (!found) {\n throw new Error(\n `Upstream \"${table}\" table is missing the \"${col}\" column`,\n );\n }\n if (found.type !== type) {\n throw new Error(\n `Upstream \"${table}.${col}\" column is a ${found.type} type but must be a ${type} type.`,\n );\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"change-source.js","names":["#lc","#upstreamUri","#shard","#replicationConfig","#startStream"],"sources":["../../../../../../../zero-cache/src/services/change-source/custom/change-source.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {WebSocket} from 'ws';\nimport {assert, unreachable} from '../../../../../shared/src/asserts.ts';\nimport {\n stringify,\n type JSONObject,\n} from '../../../../../shared/src/bigint-json.ts';\nimport {deepEqual} from '../../../../../shared/src/json.ts';\nimport type {SchemaValue} from '../../../../../zero-schema/src/table-schema.ts';\nimport {Database} from '../../../../../zqlite/src/db.ts';\nimport {computeZqlSpecs} from '../../../db/lite-tables.ts';\nimport {StatementRunner} from '../../../db/statements.ts';\nimport type {ShardConfig, ShardID} from '../../../types/shards.ts';\nimport {stream} from '../../../types/streams.ts';\nimport {\n AutoResetSignal,\n type ReplicationConfig,\n} from '../../change-streamer/schema/tables.ts';\nimport {ChangeProcessor} from '../../replicator/change-processor.ts';\nimport {ReplicationStatusPublisher} from '../../replicator/replication-status.ts';\nimport {\n createReplicationStateTables,\n getSubscriptionState,\n initReplicationState,\n} from '../../replicator/schema/replication-state.ts';\nimport type {ChangeSource, ChangeStream} from '../change-source.ts';\nimport {\n restoreReplica,\n type InitializeResult,\n type RestoreOptions,\n} from '../common/replica-restore.ts';\nimport {initReplica} from '../common/replica-schema.ts';\nimport {changeStreamMessageSchema} from '../protocol/current/downstream.ts';\nimport {\n type BackfillRequest,\n type ChangeSourceUpstream,\n} from '../protocol/current/upstream.ts';\n\n/** Server context to store with the initial sync metadata for debugging. */\nexport type ServerContext = JSONObject;\n\n/**\n * Initializes a Custom change source before streaming changes from the\n * corresponding logical replication stream.\n */\nexport async function initializeCustomChangeSource(\n lc: LogContext,\n upstreamURI: string,\n shard: ShardConfig,\n replicaDbFile: string,\n context: ServerContext,\n {litestream, constraints}: RestoreOptions = {},\n): Promise<InitializeResult> {\n // At the moment, the custom change-source implementation does not support\n // per-RM backups (and thus does not support litestream v5 or RMv2). The\n // current proposal to support this without requiring additional upstream\n // state tracking is to:\n // - use litestream to (ltx) query a fixed pool of subfolders within the\n // backupURL (e.g. /a/, /b/, /c/, /d/, /e/)\n // - restore from the subfolder with the latest ltx file\n // - backup to the subfolder with the oldest (or non-existent) ltx file\n if (litestream?.backupURL) {\n await restoreReplica(lc, litestream, replicaDbFile, constraints);\n }\n\n let initialSynced = false;\n await initReplica(\n lc,\n `replica-${shard.appID}-${shard.shardNum}`,\n replicaDbFile,\n (log, tx) => {\n initialSynced = true;\n return initialSync(log, shard, tx, upstreamURI, context);\n },\n );\n\n const replica = new Database(lc, replicaDbFile);\n const subscriptionState = getSubscriptionState(new StatementRunner(replica));\n replica.close();\n\n if (shard.publications.length) {\n // Verify that the publications match what has been synced.\n const requested = shard.publications.toSorted();\n const replicated = subscriptionState.publications.sort();\n if (!deepEqual(requested, replicated)) {\n throw new Error(\n `Invalid ShardConfig. Requested publications [${requested}] do not match synced publications: [${replicated}]`,\n );\n }\n }\n\n const changeSource = new CustomChangeSource(\n lc,\n upstreamURI,\n shard,\n subscriptionState,\n );\n\n return {\n subscriptionState,\n changeSource,\n destinationBackupURL: litestream?.backupURL,\n // A custom change source has no upstream `replicas` table to identify\n // itself from, so the generation is the whole of the SQLite change log's\n // identity here.\n replicaID: null,\n waitForBackupBeforeServing: initialSynced,\n };\n}\n\nclass CustomChangeSource implements ChangeSource {\n readonly #lc: LogContext;\n readonly #upstreamUri: string;\n readonly #shard: ShardID;\n readonly #replicationConfig: ReplicationConfig;\n\n constructor(\n lc: LogContext,\n upstreamUri: string,\n shard: ShardID,\n replicationConfig: ReplicationConfig,\n ) {\n this.#lc = lc.withContext('component', 'change-source');\n this.#upstreamUri = upstreamUri;\n this.#shard = shard;\n this.#replicationConfig = replicationConfig;\n }\n\n initialSync(): ChangeStream {\n return this.#startStream();\n }\n\n startLagReporter() {\n return null; // Not supported for custom sources\n }\n\n stop(): Promise<void> {\n return Promise.resolve();\n }\n\n startStream(\n clientWatermark: string,\n backfillRequests: BackfillRequest[] = [],\n ): Promise<ChangeStream> {\n if (backfillRequests?.length) {\n throw new Error(\n 'backfill is yet not supported for custom change sources',\n );\n }\n return Promise.resolve(this.#startStream(clientWatermark));\n }\n\n #startStream(clientWatermark?: string): ChangeStream {\n const {publications, replicaVersion} = this.#replicationConfig;\n const {appID, shardNum} = this.#shard;\n const url = new URL(this.#upstreamUri);\n url.searchParams.set('appID', appID);\n url.searchParams.set('shardNum', String(shardNum));\n for (const pub of publications) {\n url.searchParams.append('publications', pub);\n }\n if (clientWatermark) {\n assert(\n replicaVersion.length,\n 'replicaVersion is required when clientWatermark is set',\n );\n url.searchParams.set('lastWatermark', clientWatermark);\n url.searchParams.set('replicaVersion', replicaVersion);\n }\n\n const ws = new WebSocket(url);\n const {instream, outstream} = stream(\n this.#lc,\n ws,\n changeStreamMessageSchema,\n // Upstream acks coalesce. If upstream exhibits back-pressure,\n // only the last ACK is kept / buffered.\n {coalesce: (curr: ChangeSourceUpstream) => curr},\n );\n return {changes: instream, acks: outstream};\n }\n}\n\n/**\n * Initial sync for a custom change source makes a request to the\n * change source endpoint with no `replicaVersion` or `lastWatermark`.\n * The initial transaction returned by the endpoint is treated as\n * the initial sync, and the commit watermark of that transaction\n * becomes the `replicaVersion` of the initialized replica.\n *\n * Note that this is equivalent to how the LSN of the Postgres WAL\n * at initial sync time is the `replicaVersion` (and starting\n * version for all initially-synced rows).\n */\nexport async function initialSync(\n lc: LogContext,\n shard: ShardConfig,\n tx: Database,\n upstreamURI: string,\n context: ServerContext,\n) {\n const {appID: id, publications} = shard;\n const changeSource = new CustomChangeSource(lc, upstreamURI, shard, {\n replicaVersion: '', // ignored for initialSync()\n publications,\n });\n const {changes} = changeSource.initialSync();\n\n createReplicationStateTables(tx);\n const processor = new ChangeProcessor(\n new StatementRunner(tx),\n 'initial-sync',\n (_, err) => {\n throw err;\n },\n );\n\n const statusPublisher = ReplicationStatusPublisher.forRunningTransaction(tx);\n try {\n let num = 0;\n for await (const change of changes) {\n const [tag] = change;\n switch (tag) {\n case 'begin': {\n const {commitWatermark} = change[2];\n lc.info?.(\n `initial sync of shard ${id} at replicaVersion ${commitWatermark}`,\n );\n statusPublisher.publish(\n lc,\n 'Initializing',\n `Copying upstream tables at version ${commitWatermark}`,\n 5000,\n );\n initReplicationState(\n tx,\n publications.toSorted(),\n commitWatermark,\n context,\n false,\n );\n processor.processMessage(lc, change);\n break;\n }\n case 'data':\n processor.processMessage(lc, change);\n if (++num % 1000 === 0) {\n lc.debug?.(`processed ${num} changes`);\n }\n break;\n case 'commit':\n processor.processMessage(lc, change);\n validateInitiallySyncedData(lc, tx, shard);\n lc.info?.(`finished initial-sync of ${num} changes`);\n return;\n\n case 'status':\n break; // Ignored\n // @ts-expect-error: falls through if the tag is not 'reset-required\n case 'control': {\n const {tag, message} = change[1];\n if (tag === 'reset-required') {\n throw new AutoResetSignal(\n message ?? 'auto-reset signaled by change source',\n );\n }\n }\n // falls through\n case 'rollback':\n throw new Error(\n `unexpected message during initial-sync: ${stringify(change)}`,\n );\n default:\n unreachable(change);\n }\n }\n throw new Error(\n `change source ${upstreamURI} closed before initial-sync completed`,\n );\n } catch (e) {\n await statusPublisher.publishAndThrowError(lc, 'Initializing', e);\n } finally {\n statusPublisher.stop();\n }\n}\n\n// Verify that the upstream tables expected by the sync logic\n// have been properly initialized.\nfunction getRequiredTables({\n appID,\n shardNum,\n}: ShardID): Record<string, Record<string, SchemaValue>> {\n return {\n [`${appID}_${shardNum}.clients`]: {\n clientGroupID: {type: 'string'},\n clientID: {type: 'string'},\n lastMutationID: {type: 'number'},\n userID: {type: 'string'},\n },\n [`${appID}_${shardNum}.mutations`]: {\n clientGroupID: {type: 'string'},\n clientID: {type: 'string'},\n mutationID: {type: 'number'},\n mutation: {type: 'json'},\n },\n [`${appID}.permissions`]: {\n permissions: {type: 'json'},\n hash: {type: 'string'},\n },\n };\n}\n\nfunction validateInitiallySyncedData(\n lc: LogContext,\n db: Database,\n shard: ShardID,\n) {\n const tables = computeZqlSpecs(lc, db, {includeBackfillingColumns: true});\n const required = getRequiredTables(shard);\n for (const [name, columns] of Object.entries(required)) {\n const table = tables.get(name)?.zqlSpec;\n if (!table) {\n throw new Error(\n `Upstream is missing the \"${name}\" table. (Found ${[\n ...tables.keys(),\n ]})` +\n `Please ensure that each table has a unique index over one ` +\n `or more non-null columns.`,\n );\n }\n for (const [col, {type}] of Object.entries(columns)) {\n const found = table[col];\n if (!found) {\n throw new Error(\n `Upstream \"${table}\" table is missing the \"${col}\" column`,\n );\n }\n if (found.type !== type) {\n throw new Error(\n `Upstream \"${table}.${col}\" column is a ${found.type} type but must be a ${type} type.`,\n );\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -12,6 +12,7 @@ import { getOrCreateCounter, getOrCreateHistogram } from "../../../observability
|
|
|
12
12
|
import { initReplicationState } from "../../replicator/schema/replication-state.js";
|
|
13
13
|
import { id } from "../../../types/sql.js";
|
|
14
14
|
import { connectPgClient } from "../../../types/pg.js";
|
|
15
|
+
import { CpuProfiler } from "../../../types/profiler.js";
|
|
15
16
|
import { toStateVersionString } from "./lsn.js";
|
|
16
17
|
import { READONLY } from "../../../db/mode-enum.js";
|
|
17
18
|
import { runTx } from "../../../db/run-transaction.js";
|
|
@@ -24,7 +25,6 @@ import { BinaryCopyParser, hasBinaryDecoder, makeBinaryDecoder, textCastDecoder
|
|
|
24
25
|
import { TsvParser } from "../../../db/pg-copy.js";
|
|
25
26
|
import { getTypeParsers } from "../../../db/pg-type-parser.js";
|
|
26
27
|
import { TransactionPool, importSnapshot } from "../../../db/transaction-pool.js";
|
|
27
|
-
import { CpuProfiler } from "../../../types/profiler.js";
|
|
28
28
|
import { createReplicaAndSlot } from "./replication-slots.js";
|
|
29
29
|
import { ensureShardSchema } from "./schema/init.js";
|
|
30
30
|
import { platform, tmpdir } from "node:os";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-predicate.d.ts","sourceRoot":"","sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/index-predicate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EAEd,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index-predicate.d.ts","sourceRoot":"","sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/index-predicate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EAEd,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AAmBjC,eAAO,MAAM,2BAA2B,YACtC,oBAAoB,EACpB,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,kBAAkB,EAClB,oBAAoB,EACpB,6BAA6B,CACrB,CAAC;AAEX,MAAM,MAAM,0BAA0B,GACpC,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/C,MAAM,MAAM,oBAAoB,GAC5B;IAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAA;CAAC,GAC9D;IAAC,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAA;CAAC,CAAC;AAE7E,KAAK,mBAAmB,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,GAAG;IACjE,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;CAChE,CAAC;AAEF,KAAK,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC,GAAG;IAC9D,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAAC;CACjE,CAAC;AAoEF,wBAAgB,+BAA+B,CAC7C,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,kBAAkB,GACxB,oBAAoB,CAetB"}
|
|
@@ -3,6 +3,7 @@ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
|
3
3
|
var INTEGER_RE = /^[+-]?\d+$/;
|
|
4
4
|
var WHITESPACE_RE = /\s/;
|
|
5
5
|
var OPERATOR_TOKEN_RE = /^(?:::|<>|!=|<=|>=|=|<|>)/;
|
|
6
|
+
var UNSUPPORTED_OPERATOR_TOKEN_RE = /^(?:!~\*|!~|~\*|~)/;
|
|
6
7
|
var INTEGER_TOKEN_RE = /^[+-]?\d+/;
|
|
7
8
|
var WORD_TOKEN_RE = /^[A-Za-z_][A-Za-z0-9_$]*/;
|
|
8
9
|
var SIMPLE_ESCAPES = {
|
|
@@ -80,9 +81,16 @@ var Parser = class {
|
|
|
80
81
|
not
|
|
81
82
|
};
|
|
82
83
|
}
|
|
84
|
+
if (this.#take("IN")) {
|
|
85
|
+
this.expect("(");
|
|
86
|
+
const values = this.#parseOperandList(")");
|
|
87
|
+
this.expect(")");
|
|
88
|
+
return comparisons(left, values);
|
|
89
|
+
}
|
|
83
90
|
const op = this.#tokens[this.#position]?.type;
|
|
84
91
|
if (isComparison(op)) {
|
|
85
92
|
this.#position++;
|
|
93
|
+
if (op === "=" && this.#takeIdentifier("ANY")) return comparisons(left, this.#parseLiteralArray());
|
|
86
94
|
return {
|
|
87
95
|
type: "comparison",
|
|
88
96
|
left,
|
|
@@ -92,9 +100,31 @@ var Parser = class {
|
|
|
92
100
|
}
|
|
93
101
|
const next = this.#tokens[this.#position];
|
|
94
102
|
if (next?.type === "identifier" && next.value.toUpperCase() === "COLLATE") throw new UnsupportedPredicateError("non-deterministic-collation");
|
|
103
|
+
if (next?.type === "unsupported" && UNSUPPORTED_OPERATOR_TOKEN_RE.test(next.value)) throw new UnsupportedPredicateError("unsupported-operator");
|
|
95
104
|
if (next?.type === "identifier" && unsupportedOperatorWords.has(next.value.toUpperCase())) throw new UnsupportedPredicateError("unsupported-operator");
|
|
96
105
|
return left;
|
|
97
106
|
}
|
|
107
|
+
#parseLiteralArray() {
|
|
108
|
+
this.expect("(");
|
|
109
|
+
this.#expectIdentifier("ARRAY");
|
|
110
|
+
this.expect("[");
|
|
111
|
+
const values = this.#parseOperandList("]");
|
|
112
|
+
this.expect("]");
|
|
113
|
+
if (this.#take("::")) {
|
|
114
|
+
this.#parseCastName();
|
|
115
|
+
this.expect("[");
|
|
116
|
+
this.expect("]");
|
|
117
|
+
}
|
|
118
|
+
this.expect(")");
|
|
119
|
+
return values;
|
|
120
|
+
}
|
|
121
|
+
#parseOperandList(end) {
|
|
122
|
+
const values = [];
|
|
123
|
+
if (this.#tokens[this.#position]?.type === end) return values;
|
|
124
|
+
values.push(this.#parseOperand());
|
|
125
|
+
while (this.#take(",")) values.push(this.#parseOperand());
|
|
126
|
+
return values;
|
|
127
|
+
}
|
|
98
128
|
#parseOperand() {
|
|
99
129
|
let expression = this.#parsePrimary();
|
|
100
130
|
while (this.#take("::")) expression = {
|
|
@@ -130,17 +160,27 @@ var Parser = class {
|
|
|
130
160
|
#parseCastName() {
|
|
131
161
|
const first = this.#expectIdentifier();
|
|
132
162
|
const parts = [first];
|
|
163
|
+
let separator = ".";
|
|
133
164
|
while (this.#take(".")) parts.push(this.#expectIdentifier());
|
|
134
165
|
const next = this.#tokens[this.#position];
|
|
135
|
-
if (first.toLowerCase() === "character" && next?.type === "identifier" && next.value.toLowerCase() === "varying")
|
|
166
|
+
if (first.toLowerCase() === "character" && next?.type === "identifier" && next.value.toLowerCase() === "varying") {
|
|
167
|
+
parts.push(this.#expectIdentifier());
|
|
168
|
+
separator = " ";
|
|
169
|
+
}
|
|
136
170
|
if (this.#tokens[this.#position]?.type === "(") throw new UnsupportedPredicateError("unsupported-cast");
|
|
137
|
-
return parts.join(
|
|
171
|
+
return parts.join(separator);
|
|
138
172
|
}
|
|
139
|
-
#expectIdentifier() {
|
|
173
|
+
#expectIdentifier(expected) {
|
|
140
174
|
const token = this.#tokens[this.#position++];
|
|
141
|
-
if (token?.type !== "identifier") throw new UnsupportedPredicateError("unsupported-cast");
|
|
175
|
+
if (token?.type !== "identifier" || expected !== void 0 && token.value.toUpperCase() !== expected) throw new UnsupportedPredicateError("unsupported-cast");
|
|
142
176
|
return token.value;
|
|
143
177
|
}
|
|
178
|
+
#takeIdentifier(expected) {
|
|
179
|
+
const token = this.#tokens[this.#position];
|
|
180
|
+
if (token?.type !== "identifier" || token.value.toUpperCase() !== expected) return false;
|
|
181
|
+
this.#position++;
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
144
184
|
#take(type) {
|
|
145
185
|
if (this.#tokens[this.#position]?.type !== type) return false;
|
|
146
186
|
this.#position++;
|
|
@@ -152,13 +192,24 @@ var Parser = class {
|
|
|
152
192
|
};
|
|
153
193
|
var unsupportedOperatorWords = new Set([
|
|
154
194
|
"ALL",
|
|
155
|
-
"ANY",
|
|
156
195
|
"BETWEEN",
|
|
157
196
|
"ILIKE",
|
|
158
|
-
"IN",
|
|
159
197
|
"LIKE",
|
|
160
198
|
"SIMILAR"
|
|
161
199
|
]);
|
|
200
|
+
function comparisons(left, values) {
|
|
201
|
+
if (values.length === 0) throw new UnsupportedPredicateError("unsupported-syntax");
|
|
202
|
+
const expressions = values.map((right) => ({
|
|
203
|
+
type: "comparison",
|
|
204
|
+
left,
|
|
205
|
+
op: "=",
|
|
206
|
+
right
|
|
207
|
+
}));
|
|
208
|
+
return expressions.length === 1 ? expressions[0] : {
|
|
209
|
+
type: "or",
|
|
210
|
+
expressions
|
|
211
|
+
};
|
|
212
|
+
}
|
|
162
213
|
function normalize(expression, table) {
|
|
163
214
|
switch (expression.type) {
|
|
164
215
|
case "and":
|
|
@@ -419,7 +470,16 @@ function tokenize(sql) {
|
|
|
419
470
|
i += operator.length;
|
|
420
471
|
continue;
|
|
421
472
|
}
|
|
422
|
-
|
|
473
|
+
const unsupportedOperator = UNSUPPORTED_OPERATOR_TOKEN_RE.exec(sql.slice(i))?.[0];
|
|
474
|
+
if (unsupportedOperator) {
|
|
475
|
+
tokens.push({
|
|
476
|
+
type: "unsupported",
|
|
477
|
+
value: unsupportedOperator
|
|
478
|
+
});
|
|
479
|
+
i += unsupportedOperator.length;
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
if (char === "(" || char === ")" || char === "[" || char === "]" || char === "," || char === ".") {
|
|
423
483
|
tokens.push({ type: char });
|
|
424
484
|
i++;
|
|
425
485
|
continue;
|
|
@@ -448,7 +508,7 @@ function tokenize(sql) {
|
|
|
448
508
|
literalType: "null",
|
|
449
509
|
value: null
|
|
450
510
|
});
|
|
451
|
-
else if (keyword === "AND" || keyword === "OR" || keyword === "NOT" || keyword === "IS") tokens.push({ type: keyword });
|
|
511
|
+
else if (keyword === "AND" || keyword === "OR" || keyword === "NOT" || keyword === "IN" || keyword === "IS") tokens.push({ type: keyword });
|
|
452
512
|
else tokens.push({
|
|
453
513
|
type: "identifier",
|
|
454
514
|
value: word.toLowerCase()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-predicate.js","names":["#tokens","#parseOr","#parseAnd","#take","#parseNot","#parseComparison","#parseOperand","#position","#parsePrimary","#parseCastName","#expectIdentifier"],"sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/index-predicate.ts"],"sourcesContent":["import * as PostgresTypeClass from '../../../../db/postgres-type-class-enum.ts';\nimport type {\n IndexPredicate,\n IndexPredicateValue,\n PublishedTableSpec,\n} from '../../../../db/specs.ts';\n\nconst UUID_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;\nconst INTEGER_RE = /^[+-]?\\d+$/;\nconst WHITESPACE_RE = /\\s/;\nconst OPERATOR_TOKEN_RE = /^(?:::|<>|!=|<=|>=|=|<|>)/;\nconst INTEGER_TOKEN_RE = /^[+-]?\\d+/;\nconst WORD_TOKEN_RE = /^[A-Za-z_][A-Za-z0-9_$]*/;\nconst SIMPLE_ESCAPES: Readonly<Record<string, string>> = {\n b: '\\b',\n f: '\\f',\n n: '\\n',\n r: '\\r',\n t: '\\t',\n v: '\\v',\n};\n\nexport const unsupportedPredicateReasons = [\n 'unpublished-column',\n 'unsupported-type',\n 'unsupported-operator',\n 'unsupported-function',\n 'unsupported-cast',\n 'unsupported-syntax',\n 'non-deterministic-collation',\n] as const;\n\nexport type UnsupportedPredicateReason =\n (typeof unsupportedPredicateReasons)[number];\n\nexport type PredicateTranslation =\n | {readonly supported: true; readonly predicate: IndexPredicate}\n | {readonly supported: false; readonly reason: UnsupportedPredicateReason};\n\ntype PredicateColumnSpec = PublishedTableSpec['columns'][string] & {\n readonly collationIsDeterministic?: boolean | null | undefined;\n};\n\ntype PredicateTableSpec = Omit<PublishedTableSpec, 'columns'> & {\n readonly columns: Readonly<Record<string, PredicateColumnSpec>>;\n};\n\ntype Expr =\n | {readonly type: 'column'; readonly name: string}\n | {\n readonly type: 'literal';\n readonly value: boolean | string | null;\n readonly literalType: 'boolean' | 'integer' | 'string' | 'null';\n }\n | {readonly type: 'cast'; readonly expression: Expr; readonly cast: string}\n | {readonly type: 'not'; readonly expression: Expr}\n | {\n readonly type: 'and' | 'or';\n readonly expressions: readonly Expr[];\n }\n | {\n readonly type: 'comparison';\n readonly left: Expr;\n readonly op: '=' | '<>' | '!=' | '<' | '<=' | '>' | '>=';\n readonly right: Expr;\n }\n | {\n readonly type: 'null-test';\n readonly expression: Expr;\n readonly not: boolean;\n };\n\ntype Token =\n | {readonly type: 'identifier'; readonly value: string}\n | {\n readonly type: 'literal';\n readonly value: boolean | string | null;\n readonly literalType: 'boolean' | 'integer' | 'string' | 'null';\n }\n | {\n readonly type:\n | '('\n | ')'\n | '.'\n | '::'\n | '='\n | '<>'\n | '!='\n | '<'\n | '<='\n | '>'\n | '>='\n | 'AND'\n | 'OR'\n | 'NOT'\n | 'IS'\n | 'EOF';\n }\n | {readonly type: 'unsupported'; readonly value: string};\n\nclass UnsupportedPredicateError extends Error {\n readonly reason: UnsupportedPredicateReason;\n\n constructor(reason: UnsupportedPredicateReason) {\n super(reason);\n this.reason = reason;\n }\n}\n\nexport function translatePostgresIndexPredicate(\n sql: string,\n table: PredicateTableSpec,\n): PredicateTranslation {\n try {\n const parser = new Parser(tokenize(sql));\n const expression = parser.parse();\n parser.expect('EOF');\n return {supported: true, predicate: normalize(expression, table)};\n } catch (error) {\n return {\n supported: false,\n reason:\n error instanceof UnsupportedPredicateError\n ? error.reason\n : 'unsupported-syntax',\n };\n }\n}\n\nclass Parser {\n readonly #tokens: readonly Token[];\n #position = 0;\n\n constructor(tokens: readonly Token[]) {\n this.#tokens = tokens;\n }\n\n parse(): Expr {\n return this.#parseOr();\n }\n\n #parseOr(): Expr {\n const expressions = [this.#parseAnd()];\n while (this.#take('OR')) expressions.push(this.#parseAnd());\n return expressions.length === 1\n ? expressions[0]\n : {type: 'or', expressions};\n }\n\n #parseAnd(): Expr {\n const expressions = [this.#parseNot()];\n while (this.#take('AND')) expressions.push(this.#parseNot());\n return expressions.length === 1\n ? expressions[0]\n : {type: 'and', expressions};\n }\n\n #parseNot(): Expr {\n if (this.#take('NOT')) {\n return {type: 'not', expression: this.#parseNot()};\n }\n return this.#parseComparison();\n }\n\n #parseComparison(): Expr {\n const left = this.#parseOperand();\n if (this.#take('IS')) {\n const not = this.#take('NOT');\n const token = this.#tokens[this.#position++];\n if (token?.type !== 'literal' || token.literalType !== 'null') {\n throw new UnsupportedPredicateError('unsupported-operator');\n }\n return {type: 'null-test', expression: left, not};\n }\n const op = this.#tokens[this.#position]?.type;\n if (isComparison(op)) {\n this.#position++;\n return {\n type: 'comparison',\n left,\n op,\n right: this.#parseOperand(),\n };\n }\n const next = this.#tokens[this.#position];\n if (next?.type === 'identifier' && next.value.toUpperCase() === 'COLLATE') {\n throw new UnsupportedPredicateError('non-deterministic-collation');\n }\n if (\n next?.type === 'identifier' &&\n unsupportedOperatorWords.has(next.value.toUpperCase())\n ) {\n throw new UnsupportedPredicateError('unsupported-operator');\n }\n return left;\n }\n\n #parseOperand(): Expr {\n let expression = this.#parsePrimary();\n while (this.#take('::')) {\n expression = {\n type: 'cast',\n expression,\n cast: this.#parseCastName(),\n };\n }\n return expression;\n }\n\n #parsePrimary(): Expr {\n if (this.#take('(')) {\n const expression = this.#parseOr();\n this.expect(')');\n return expression;\n }\n const token = this.#tokens[this.#position++];\n if (token?.type === 'literal') {\n return {\n type: 'literal',\n value: token.value,\n literalType: token.literalType,\n };\n }\n if (token?.type === 'identifier') {\n if (this.#tokens[this.#position]?.type === '(') {\n throw new UnsupportedPredicateError('unsupported-function');\n }\n if (this.#tokens[this.#position]?.type === '.') {\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n if (token.value.toUpperCase() === 'COLLATE') {\n throw new UnsupportedPredicateError('non-deterministic-collation');\n }\n return {type: 'column', name: token.value};\n }\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n\n #parseCastName(): string {\n const first = this.#expectIdentifier();\n const parts = [first];\n while (this.#take('.')) parts.push(this.#expectIdentifier());\n const next = this.#tokens[this.#position];\n if (\n first.toLowerCase() === 'character' &&\n next?.type === 'identifier' &&\n next.value.toLowerCase() === 'varying'\n ) {\n parts.push(this.#expectIdentifier());\n }\n if (this.#tokens[this.#position]?.type === '(') {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n return parts.join('.');\n }\n\n #expectIdentifier(): string {\n const token = this.#tokens[this.#position++];\n if (token?.type !== 'identifier') {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n return token.value;\n }\n\n #take(type: Token['type']): boolean {\n if (this.#tokens[this.#position]?.type !== type) return false;\n this.#position++;\n return true;\n }\n\n expect(type: Token['type']): void {\n if (!this.#take(type)) {\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n }\n}\n\nconst unsupportedOperatorWords = new Set([\n 'ALL',\n 'ANY',\n 'BETWEEN',\n 'ILIKE',\n 'IN',\n 'LIKE',\n 'SIMILAR',\n]);\n\nfunction normalize(\n expression: Expr,\n table: PredicateTableSpec,\n): IndexPredicate {\n switch (expression.type) {\n case 'and':\n case 'or': {\n const conditions = expression.expressions.map(expr =>\n normalize(expr, table),\n );\n return {\n type: expression.type,\n conditions: conditions.flatMap(condition =>\n condition.type === expression.type\n ? condition.conditions\n : [condition],\n ),\n };\n }\n case 'not': {\n const booleanColumn = resolveBareBooleanColumn(\n expression.expression,\n table,\n );\n if (booleanColumn) {\n return booleanComparison(booleanColumn, false);\n }\n return {type: 'not', condition: normalize(expression.expression, table)};\n }\n case 'column': {\n const column = resolveColumn(expression, table);\n if (column.category !== 'boolean') {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n return booleanComparison(column.name, true);\n }\n case 'cast': {\n const column = resolveColumnOperand(expression, table);\n if (column.category !== 'boolean') {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n return booleanComparison(column.name, true);\n }\n case 'null-test': {\n const column = resolveNullTestColumn(expression.expression, table);\n return {\n type: 'null-test',\n column,\n op: expression.not ? 'IS NOT NULL' : 'IS NULL',\n };\n }\n case 'comparison':\n return normalizeComparison(expression, table);\n case 'literal':\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n}\n\nfunction resolveNullTestColumn(\n expression: Expr,\n table: PredicateTableSpec,\n): string {\n if (expression.type === 'cast') {\n return resolveColumnOperand(expression, table).name;\n }\n if (expression.type === 'column' && !table.columns[expression.name]) {\n throw new UnsupportedPredicateError('unpublished-column');\n }\n if (expression.type === 'column') return expression.name;\n throw new UnsupportedPredicateError('unsupported-syntax');\n}\n\nfunction resolveBareBooleanColumn(\n expression: Expr,\n table: PredicateTableSpec,\n): string | undefined {\n if (expression.type !== 'column' && expression.type !== 'cast') {\n return undefined;\n }\n const column = resolveColumnOperand(expression, table);\n return column.category === 'boolean' ? column.name : undefined;\n}\n\nfunction booleanComparison(column: string, value: boolean): IndexPredicate {\n return {\n type: 'comparison',\n column,\n op: '=',\n value: {type: 'boolean', value},\n };\n}\n\ntype ColumnCategory =\n | {readonly category: 'boolean'; readonly name: string}\n | {\n readonly category: 'integer';\n readonly name: string;\n readonly rank: 0 | 1 | 2;\n }\n | {readonly category: 'text'; readonly name: string}\n | {\n readonly category: 'enum';\n readonly name: string;\n readonly enumName: string;\n }\n | {readonly category: 'uuid'; readonly name: string};\n\nfunction normalizeComparison(\n expression: Extract<Expr, {type: 'comparison'}>,\n table: PredicateTableSpec,\n): IndexPredicate {\n const column = resolveColumnOperand(expression.left, table);\n const op = expression.op === '!=' ? '<>' : expression.op;\n if (column.category !== 'integer' && op !== '=' && op !== '<>') {\n throw new UnsupportedPredicateError('unsupported-operator');\n }\n const value = resolveLiteral(expression.right, column);\n return {type: 'comparison', column: column.name, op, value};\n}\n\nfunction resolveColumnOperand(\n expression: Expr,\n table: PredicateTableSpec,\n): ColumnCategory {\n if (expression.type === 'column') return resolveColumn(expression, table);\n if (expression.type !== 'cast') {\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n const column = resolveColumnOperand(expression.expression, table);\n const cast = canonicalTypeName(expression.cast);\n switch (column.category) {\n case 'integer': {\n const rank = integerRank(cast);\n if (rank === undefined || rank < column.rank) {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n return {...column, rank};\n }\n case 'text':\n if (cast !== 'text' && cast !== 'varchar') {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n return column;\n case 'boolean':\n if (cast !== 'bool') {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n return column;\n case 'uuid':\n if (cast !== 'uuid') {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n return column;\n case 'enum':\n if (lastTypeName(expression.cast) !== column.enumName.toLowerCase()) {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n return column;\n }\n}\n\nfunction resolveColumn(\n expression: Extract<Expr, {type: 'column'}>,\n table: PredicateTableSpec,\n): ColumnCategory {\n const spec = table.columns[expression.name];\n if (!spec) throw new UnsupportedPredicateError('unpublished-column');\n const dataType = canonicalTypeName(spec.dataType);\n if (dataType === 'bool') return {category: 'boolean', name: expression.name};\n const rank = integerRank(dataType);\n if (rank !== undefined) {\n return {category: 'integer', name: expression.name, rank};\n }\n if (dataType === 'text' || dataType === 'varchar') {\n if (spec.collationIsDeterministic === false) {\n throw new UnsupportedPredicateError('non-deterministic-collation');\n }\n return {category: 'text', name: expression.name};\n }\n if (dataType === 'uuid') return {category: 'uuid', name: expression.name};\n if (spec.pgTypeClass === PostgresTypeClass.Enum) {\n return {\n category: 'enum',\n name: expression.name,\n enumName: spec.dataType.toLowerCase(),\n };\n }\n throw new UnsupportedPredicateError('unsupported-type');\n}\n\nfunction resolveLiteral(\n expression: Expr,\n column: ColumnCategory,\n): IndexPredicateValue {\n let literal = expression;\n let cast: string | undefined;\n while (literal.type === 'cast') {\n cast = literal.cast;\n literal = literal.expression;\n }\n if (literal.type !== 'literal' || literal.value === null) {\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n\n switch (column.category) {\n case 'boolean': {\n validateLiteralCast(cast, new Set(['bool']));\n if (literal.literalType !== 'boolean') {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n return {type: 'boolean', value: literal.value as boolean};\n }\n case 'integer': {\n if (cast !== undefined) {\n const castRank = integerRank(canonicalTypeName(cast));\n if (castRank === undefined || castRank > column.rank) {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n }\n if (\n literal.literalType !== 'integer' &&\n !(literal.literalType === 'string' && cast !== undefined)\n ) {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n return {\n type: 'integer',\n value: canonicalInteger(String(literal.value), column.rank),\n };\n }\n case 'text':\n validateLiteralCast(cast, new Set(['text', 'varchar']));\n if (literal.literalType !== 'string') {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n return {type: 'string', value: String(literal.value)};\n case 'enum':\n if (\n cast !== undefined &&\n lastTypeName(cast) !== column.enumName.toLowerCase()\n ) {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n if (literal.literalType !== 'string') {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n return {type: 'string', value: String(literal.value)};\n case 'uuid': {\n validateLiteralCast(cast, new Set(['uuid']));\n const value = String(literal.value).toLowerCase();\n if (literal.literalType !== 'string' || !UUID_RE.test(value)) {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n return {type: 'string', value};\n }\n }\n}\n\nfunction validateLiteralCast(\n cast: string | undefined,\n allowed: ReadonlySet<string>,\n): void {\n if (cast !== undefined && !allowed.has(canonicalTypeName(cast))) {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n}\n\nfunction canonicalInteger(value: string, rank: 0 | 1 | 2): string {\n if (!INTEGER_RE.test(value)) {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n let integer: bigint;\n try {\n integer = BigInt(value);\n } catch {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n const bounds = [\n [-32768n, 32767n],\n [-2147483648n, 2147483647n],\n [-9223372036854775808n, 9223372036854775807n],\n ] as const;\n if (integer < bounds[rank][0] || integer > bounds[rank][1]) {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n return integer.toString();\n}\n\nfunction canonicalTypeName(type: string): string {\n const name = lastTypeName(type).replace(/\\s+/g, ' ');\n switch (name) {\n case 'boolean':\n return 'bool';\n case 'smallint':\n return 'int2';\n case 'integer':\n return 'int4';\n case 'bigint':\n return 'int8';\n case 'character varying':\n return 'varchar';\n default:\n return name;\n }\n}\n\nfunction lastTypeName(type: string): string {\n return type.split('.').at(-1)?.toLowerCase() ?? '';\n}\n\nfunction integerRank(type: string): 0 | 1 | 2 | undefined {\n switch (type) {\n case 'int2':\n return 0;\n case 'int4':\n return 1;\n case 'int8':\n return 2;\n default:\n return undefined;\n }\n}\n\nfunction isComparison(\n type: Token['type'] | undefined,\n): type is '=' | '<>' | '!=' | '<' | '<=' | '>' | '>=' {\n return (\n type === '=' ||\n type === '<>' ||\n type === '!=' ||\n type === '<' ||\n type === '<=' ||\n type === '>' ||\n type === '>='\n );\n}\n\nfunction tokenize(sql: string): readonly Token[] {\n const tokens: Token[] = [];\n for (let i = 0; i < sql.length;) {\n const char = sql[i];\n if (WHITESPACE_RE.test(char)) {\n i++;\n continue;\n }\n if (char === '\"') {\n const result = readQuoted(sql, i, '\"', false);\n tokens.push({type: 'identifier', value: result.value});\n i = result.end;\n continue;\n }\n const escapeString = (char === 'E' || char === 'e') && sql[i + 1] === \"'\";\n if (char === \"'\" || escapeString) {\n const result = readQuoted(\n sql,\n i + (escapeString ? 1 : 0),\n \"'\",\n escapeString,\n );\n tokens.push({\n type: 'literal',\n literalType: 'string',\n value: result.value,\n });\n i = result.end;\n continue;\n }\n const operator = OPERATOR_TOKEN_RE.exec(sql.slice(i))?.[0];\n if (operator) {\n tokens.push({\n type: operator as '::' | '=' | '<>' | '!=' | '<' | '<=' | '>' | '>=',\n });\n i += operator.length;\n continue;\n }\n if (char === '(' || char === ')' || char === '.') {\n tokens.push({type: char});\n i++;\n continue;\n }\n const integer = INTEGER_TOKEN_RE.exec(sql.slice(i))?.[0];\n if (integer) {\n tokens.push({type: 'literal', literalType: 'integer', value: integer});\n i += integer.length;\n continue;\n }\n const word = WORD_TOKEN_RE.exec(sql.slice(i))?.[0];\n if (word) {\n const keyword = word.toUpperCase();\n if (unsupportedOperatorWords.has(keyword)) {\n throw new UnsupportedPredicateError('unsupported-operator');\n }\n if (keyword === 'TRUE' || keyword === 'FALSE') {\n tokens.push({\n type: 'literal',\n literalType: 'boolean',\n value: keyword === 'TRUE',\n });\n } else if (keyword === 'NULL') {\n tokens.push({type: 'literal', literalType: 'null', value: null});\n } else if (\n keyword === 'AND' ||\n keyword === 'OR' ||\n keyword === 'NOT' ||\n keyword === 'IS'\n ) {\n tokens.push({type: keyword});\n } else {\n tokens.push({type: 'identifier', value: word.toLowerCase()});\n }\n i += word.length;\n continue;\n }\n tokens.push({type: 'unsupported', value: char});\n i++;\n }\n tokens.push({type: 'EOF'});\n return tokens;\n}\n\nfunction readQuoted(\n sql: string,\n start: number,\n quote: '\"' | \"'\",\n escapes: boolean,\n): {readonly value: string; readonly end: number} {\n let value = '';\n for (let i = start + 1; i < sql.length; i++) {\n const char = sql[i];\n if (char === quote) {\n if (sql[i + 1] === quote) {\n value += quote;\n i++;\n } else {\n return {value, end: i + 1};\n }\n } else if (escapes && char === '\\\\') {\n const escape = sql[++i];\n if (escape === undefined) {\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n if (escape === 'x') {\n const hex = readEscapeDigits(sql, i + 1, 2, 16, 1);\n value += String.fromCharCode(hex.value);\n i = hex.end - 1;\n } else if (escape === 'u' || escape === 'U') {\n const hex = readEscapeDigits(sql, i + 1, escape === 'u' ? 4 : 8, 16);\n try {\n value += String.fromCodePoint(hex.value);\n } catch {\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n i = hex.end - 1;\n } else if (escape >= '0' && escape <= '7') {\n let octal = escape;\n while (\n octal.length < 3 &&\n sql[i + 1] !== undefined &&\n sql[i + 1] >= '0' &&\n sql[i + 1] <= '7'\n ) {\n octal += sql[++i];\n }\n value += String.fromCharCode(Number.parseInt(octal, 8));\n } else if (escape === '\\n') {\n // A backslash-newline pair is a line continuation.\n } else if (escape === '\\r' && sql[i + 1] === '\\n') {\n i++;\n } else {\n value += SIMPLE_ESCAPES[escape] ?? escape;\n }\n } else {\n value += char;\n }\n }\n throw new UnsupportedPredicateError('unsupported-syntax');\n}\n\nfunction readEscapeDigits(\n sql: string,\n start: number,\n length: number,\n radix: 16,\n minLength = length,\n): {readonly value: number; readonly end: number} {\n let end = start;\n while (end < start + length) {\n const digit = sql[end];\n if (\n digit === undefined ||\n !(\n (digit >= '0' && digit <= '9') ||\n (digit.toLowerCase() >= 'a' && digit.toLowerCase() <= 'f')\n )\n ) {\n break;\n }\n end++;\n }\n const digits = sql.slice(start, end);\n if (digits.length < minLength) {\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n return {value: Number.parseInt(digits, radix), end};\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-predicate.js","names":["#tokens","#parseOr","#parseAnd","#take","#parseNot","#parseComparison","#parseOperand","#position","#parseOperandList","#takeIdentifier","#parseLiteralArray","#expectIdentifier","#parseCastName","#parsePrimary"],"sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/index-predicate.ts"],"sourcesContent":["import * as PostgresTypeClass from '../../../../db/postgres-type-class-enum.ts';\nimport type {\n IndexPredicate,\n IndexPredicateValue,\n PublishedTableSpec,\n} from '../../../../db/specs.ts';\n\nconst UUID_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;\nconst INTEGER_RE = /^[+-]?\\d+$/;\nconst WHITESPACE_RE = /\\s/;\nconst OPERATOR_TOKEN_RE = /^(?:::|<>|!=|<=|>=|=|<|>)/;\nconst UNSUPPORTED_OPERATOR_TOKEN_RE = /^(?:!~\\*|!~|~\\*|~)/;\nconst INTEGER_TOKEN_RE = /^[+-]?\\d+/;\nconst WORD_TOKEN_RE = /^[A-Za-z_][A-Za-z0-9_$]*/;\nconst SIMPLE_ESCAPES: Readonly<Record<string, string>> = {\n b: '\\b',\n f: '\\f',\n n: '\\n',\n r: '\\r',\n t: '\\t',\n v: '\\v',\n};\n\nexport const unsupportedPredicateReasons = [\n 'unpublished-column',\n 'unsupported-type',\n 'unsupported-operator',\n 'unsupported-function',\n 'unsupported-cast',\n 'unsupported-syntax',\n 'non-deterministic-collation',\n] as const;\n\nexport type UnsupportedPredicateReason =\n (typeof unsupportedPredicateReasons)[number];\n\nexport type PredicateTranslation =\n | {readonly supported: true; readonly predicate: IndexPredicate}\n | {readonly supported: false; readonly reason: UnsupportedPredicateReason};\n\ntype PredicateColumnSpec = PublishedTableSpec['columns'][string] & {\n readonly collationIsDeterministic?: boolean | null | undefined;\n};\n\ntype PredicateTableSpec = Omit<PublishedTableSpec, 'columns'> & {\n readonly columns: Readonly<Record<string, PredicateColumnSpec>>;\n};\n\ntype Expr =\n | {readonly type: 'column'; readonly name: string}\n | {\n readonly type: 'literal';\n readonly value: boolean | string | null;\n readonly literalType: 'boolean' | 'integer' | 'string' | 'null';\n }\n | {readonly type: 'cast'; readonly expression: Expr; readonly cast: string}\n | {readonly type: 'not'; readonly expression: Expr}\n | {\n readonly type: 'and' | 'or';\n readonly expressions: readonly Expr[];\n }\n | {\n readonly type: 'comparison';\n readonly left: Expr;\n readonly op: '=' | '<>' | '!=' | '<' | '<=' | '>' | '>=';\n readonly right: Expr;\n }\n | {\n readonly type: 'null-test';\n readonly expression: Expr;\n readonly not: boolean;\n };\n\ntype Token =\n | {readonly type: 'identifier'; readonly value: string}\n | {\n readonly type: 'literal';\n readonly value: boolean | string | null;\n readonly literalType: 'boolean' | 'integer' | 'string' | 'null';\n }\n | {\n readonly type:\n | '('\n | ')'\n | '['\n | ']'\n | ','\n | '.'\n | '::'\n | '='\n | '<>'\n | '!='\n | '<'\n | '<='\n | '>'\n | '>='\n | 'AND'\n | 'OR'\n | 'NOT'\n | 'IN'\n | 'IS'\n | 'EOF';\n }\n | {readonly type: 'unsupported'; readonly value: string};\n\nclass UnsupportedPredicateError extends Error {\n readonly reason: UnsupportedPredicateReason;\n\n constructor(reason: UnsupportedPredicateReason) {\n super(reason);\n this.reason = reason;\n }\n}\n\nexport function translatePostgresIndexPredicate(\n sql: string,\n table: PredicateTableSpec,\n): PredicateTranslation {\n try {\n const parser = new Parser(tokenize(sql));\n const expression = parser.parse();\n parser.expect('EOF');\n return {supported: true, predicate: normalize(expression, table)};\n } catch (error) {\n return {\n supported: false,\n reason:\n error instanceof UnsupportedPredicateError\n ? error.reason\n : 'unsupported-syntax',\n };\n }\n}\n\nclass Parser {\n readonly #tokens: readonly Token[];\n #position = 0;\n\n constructor(tokens: readonly Token[]) {\n this.#tokens = tokens;\n }\n\n parse(): Expr {\n return this.#parseOr();\n }\n\n #parseOr(): Expr {\n const expressions = [this.#parseAnd()];\n while (this.#take('OR')) expressions.push(this.#parseAnd());\n return expressions.length === 1\n ? expressions[0]\n : {type: 'or', expressions};\n }\n\n #parseAnd(): Expr {\n const expressions = [this.#parseNot()];\n while (this.#take('AND')) expressions.push(this.#parseNot());\n return expressions.length === 1\n ? expressions[0]\n : {type: 'and', expressions};\n }\n\n #parseNot(): Expr {\n if (this.#take('NOT')) {\n return {type: 'not', expression: this.#parseNot()};\n }\n return this.#parseComparison();\n }\n\n #parseComparison(): Expr {\n const left = this.#parseOperand();\n if (this.#take('IS')) {\n const not = this.#take('NOT');\n const token = this.#tokens[this.#position++];\n if (token?.type !== 'literal' || token.literalType !== 'null') {\n throw new UnsupportedPredicateError('unsupported-operator');\n }\n return {type: 'null-test', expression: left, not};\n }\n if (this.#take('IN')) {\n this.expect('(');\n const values = this.#parseOperandList(')');\n this.expect(')');\n return comparisons(left, values);\n }\n const op = this.#tokens[this.#position]?.type;\n if (isComparison(op)) {\n this.#position++;\n if (op === '=' && this.#takeIdentifier('ANY')) {\n return comparisons(left, this.#parseLiteralArray());\n }\n return {\n type: 'comparison',\n left,\n op,\n right: this.#parseOperand(),\n };\n }\n const next = this.#tokens[this.#position];\n if (next?.type === 'identifier' && next.value.toUpperCase() === 'COLLATE') {\n throw new UnsupportedPredicateError('non-deterministic-collation');\n }\n if (\n next?.type === 'unsupported' &&\n UNSUPPORTED_OPERATOR_TOKEN_RE.test(next.value)\n ) {\n throw new UnsupportedPredicateError('unsupported-operator');\n }\n if (\n next?.type === 'identifier' &&\n unsupportedOperatorWords.has(next.value.toUpperCase())\n ) {\n throw new UnsupportedPredicateError('unsupported-operator');\n }\n return left;\n }\n\n #parseLiteralArray(): readonly Expr[] {\n this.expect('(');\n this.#expectIdentifier('ARRAY');\n this.expect('[');\n const values = this.#parseOperandList(']');\n this.expect(']');\n if (this.#take('::')) {\n this.#parseCastName();\n this.expect('[');\n this.expect(']');\n }\n this.expect(')');\n return values;\n }\n\n #parseOperandList(end: ')' | ']'): readonly Expr[] {\n const values: Expr[] = [];\n if (this.#tokens[this.#position]?.type === end) return values;\n values.push(this.#parseOperand());\n while (this.#take(',')) values.push(this.#parseOperand());\n return values;\n }\n\n #parseOperand(): Expr {\n let expression = this.#parsePrimary();\n while (this.#take('::')) {\n expression = {\n type: 'cast',\n expression,\n cast: this.#parseCastName(),\n };\n }\n return expression;\n }\n\n #parsePrimary(): Expr {\n if (this.#take('(')) {\n const expression = this.#parseOr();\n this.expect(')');\n return expression;\n }\n const token = this.#tokens[this.#position++];\n if (token?.type === 'literal') {\n return {\n type: 'literal',\n value: token.value,\n literalType: token.literalType,\n };\n }\n if (token?.type === 'identifier') {\n if (this.#tokens[this.#position]?.type === '(') {\n throw new UnsupportedPredicateError('unsupported-function');\n }\n if (this.#tokens[this.#position]?.type === '.') {\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n if (token.value.toUpperCase() === 'COLLATE') {\n throw new UnsupportedPredicateError('non-deterministic-collation');\n }\n return {type: 'column', name: token.value};\n }\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n\n #parseCastName(): string {\n const first = this.#expectIdentifier();\n const parts = [first];\n let separator = '.';\n while (this.#take('.')) parts.push(this.#expectIdentifier());\n const next = this.#tokens[this.#position];\n if (\n first.toLowerCase() === 'character' &&\n next?.type === 'identifier' &&\n next.value.toLowerCase() === 'varying'\n ) {\n parts.push(this.#expectIdentifier());\n separator = ' ';\n }\n if (this.#tokens[this.#position]?.type === '(') {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n return parts.join(separator);\n }\n\n #expectIdentifier(expected?: string): string {\n const token = this.#tokens[this.#position++];\n if (\n token?.type !== 'identifier' ||\n (expected !== undefined && token.value.toUpperCase() !== expected)\n ) {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n return token.value;\n }\n\n #takeIdentifier(expected: string): boolean {\n const token = this.#tokens[this.#position];\n if (\n token?.type !== 'identifier' ||\n token.value.toUpperCase() !== expected\n ) {\n return false;\n }\n this.#position++;\n return true;\n }\n\n #take(type: Token['type']): boolean {\n if (this.#tokens[this.#position]?.type !== type) return false;\n this.#position++;\n return true;\n }\n\n expect(type: Token['type']): void {\n if (!this.#take(type)) {\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n }\n}\n\nconst unsupportedOperatorWords = new Set([\n 'ALL',\n 'BETWEEN',\n 'ILIKE',\n 'LIKE',\n 'SIMILAR',\n]);\n\nfunction comparisons(left: Expr, values: readonly Expr[]): Expr {\n if (values.length === 0) {\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n const expressions = values.map(\n right => ({type: 'comparison', left, op: '=', right}) as const,\n );\n return expressions.length === 1 ? expressions[0] : {type: 'or', expressions};\n}\n\nfunction normalize(\n expression: Expr,\n table: PredicateTableSpec,\n): IndexPredicate {\n switch (expression.type) {\n case 'and':\n case 'or': {\n const conditions = expression.expressions.map(expr =>\n normalize(expr, table),\n );\n return {\n type: expression.type,\n conditions: conditions.flatMap(condition =>\n condition.type === expression.type\n ? condition.conditions\n : [condition],\n ),\n };\n }\n case 'not': {\n const booleanColumn = resolveBareBooleanColumn(\n expression.expression,\n table,\n );\n if (booleanColumn) {\n return booleanComparison(booleanColumn, false);\n }\n return {type: 'not', condition: normalize(expression.expression, table)};\n }\n case 'column': {\n const column = resolveColumn(expression, table);\n if (column.category !== 'boolean') {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n return booleanComparison(column.name, true);\n }\n case 'cast': {\n const column = resolveColumnOperand(expression, table);\n if (column.category !== 'boolean') {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n return booleanComparison(column.name, true);\n }\n case 'null-test': {\n const column = resolveNullTestColumn(expression.expression, table);\n return {\n type: 'null-test',\n column,\n op: expression.not ? 'IS NOT NULL' : 'IS NULL',\n };\n }\n case 'comparison':\n return normalizeComparison(expression, table);\n case 'literal':\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n}\n\nfunction resolveNullTestColumn(\n expression: Expr,\n table: PredicateTableSpec,\n): string {\n if (expression.type === 'cast') {\n return resolveColumnOperand(expression, table).name;\n }\n if (expression.type === 'column' && !table.columns[expression.name]) {\n throw new UnsupportedPredicateError('unpublished-column');\n }\n if (expression.type === 'column') return expression.name;\n throw new UnsupportedPredicateError('unsupported-syntax');\n}\n\nfunction resolveBareBooleanColumn(\n expression: Expr,\n table: PredicateTableSpec,\n): string | undefined {\n if (expression.type !== 'column' && expression.type !== 'cast') {\n return undefined;\n }\n const column = resolveColumnOperand(expression, table);\n return column.category === 'boolean' ? column.name : undefined;\n}\n\nfunction booleanComparison(column: string, value: boolean): IndexPredicate {\n return {\n type: 'comparison',\n column,\n op: '=',\n value: {type: 'boolean', value},\n };\n}\n\ntype ColumnCategory =\n | {readonly category: 'boolean'; readonly name: string}\n | {\n readonly category: 'integer';\n readonly name: string;\n readonly rank: 0 | 1 | 2;\n }\n | {readonly category: 'text'; readonly name: string}\n | {\n readonly category: 'enum';\n readonly name: string;\n readonly enumName: string;\n }\n | {readonly category: 'uuid'; readonly name: string};\n\nfunction normalizeComparison(\n expression: Extract<Expr, {type: 'comparison'}>,\n table: PredicateTableSpec,\n): IndexPredicate {\n const column = resolveColumnOperand(expression.left, table);\n const op = expression.op === '!=' ? '<>' : expression.op;\n if (column.category !== 'integer' && op !== '=' && op !== '<>') {\n throw new UnsupportedPredicateError('unsupported-operator');\n }\n const value = resolveLiteral(expression.right, column);\n return {type: 'comparison', column: column.name, op, value};\n}\n\nfunction resolveColumnOperand(\n expression: Expr,\n table: PredicateTableSpec,\n): ColumnCategory {\n if (expression.type === 'column') return resolveColumn(expression, table);\n if (expression.type !== 'cast') {\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n const column = resolveColumnOperand(expression.expression, table);\n const cast = canonicalTypeName(expression.cast);\n switch (column.category) {\n case 'integer': {\n const rank = integerRank(cast);\n if (rank === undefined || rank < column.rank) {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n return {...column, rank};\n }\n case 'text':\n if (cast !== 'text' && cast !== 'varchar') {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n return column;\n case 'boolean':\n if (cast !== 'bool') {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n return column;\n case 'uuid':\n if (cast !== 'uuid') {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n return column;\n case 'enum':\n if (lastTypeName(expression.cast) !== column.enumName.toLowerCase()) {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n return column;\n }\n}\n\nfunction resolveColumn(\n expression: Extract<Expr, {type: 'column'}>,\n table: PredicateTableSpec,\n): ColumnCategory {\n const spec = table.columns[expression.name];\n if (!spec) throw new UnsupportedPredicateError('unpublished-column');\n const dataType = canonicalTypeName(spec.dataType);\n if (dataType === 'bool') return {category: 'boolean', name: expression.name};\n const rank = integerRank(dataType);\n if (rank !== undefined) {\n return {category: 'integer', name: expression.name, rank};\n }\n if (dataType === 'text' || dataType === 'varchar') {\n if (spec.collationIsDeterministic === false) {\n throw new UnsupportedPredicateError('non-deterministic-collation');\n }\n return {category: 'text', name: expression.name};\n }\n if (dataType === 'uuid') return {category: 'uuid', name: expression.name};\n if (spec.pgTypeClass === PostgresTypeClass.Enum) {\n return {\n category: 'enum',\n name: expression.name,\n enumName: spec.dataType.toLowerCase(),\n };\n }\n throw new UnsupportedPredicateError('unsupported-type');\n}\n\nfunction resolveLiteral(\n expression: Expr,\n column: ColumnCategory,\n): IndexPredicateValue {\n let literal = expression;\n let cast: string | undefined;\n while (literal.type === 'cast') {\n cast = literal.cast;\n literal = literal.expression;\n }\n if (literal.type !== 'literal' || literal.value === null) {\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n\n switch (column.category) {\n case 'boolean': {\n validateLiteralCast(cast, new Set(['bool']));\n if (literal.literalType !== 'boolean') {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n return {type: 'boolean', value: literal.value as boolean};\n }\n case 'integer': {\n if (cast !== undefined) {\n const castRank = integerRank(canonicalTypeName(cast));\n if (castRank === undefined || castRank > column.rank) {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n }\n if (\n literal.literalType !== 'integer' &&\n !(literal.literalType === 'string' && cast !== undefined)\n ) {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n return {\n type: 'integer',\n value: canonicalInteger(String(literal.value), column.rank),\n };\n }\n case 'text':\n validateLiteralCast(cast, new Set(['text', 'varchar']));\n if (literal.literalType !== 'string') {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n return {type: 'string', value: String(literal.value)};\n case 'enum':\n if (\n cast !== undefined &&\n lastTypeName(cast) !== column.enumName.toLowerCase()\n ) {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n if (literal.literalType !== 'string') {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n return {type: 'string', value: String(literal.value)};\n case 'uuid': {\n validateLiteralCast(cast, new Set(['uuid']));\n const value = String(literal.value).toLowerCase();\n if (literal.literalType !== 'string' || !UUID_RE.test(value)) {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n return {type: 'string', value};\n }\n }\n}\n\nfunction validateLiteralCast(\n cast: string | undefined,\n allowed: ReadonlySet<string>,\n): void {\n if (cast !== undefined && !allowed.has(canonicalTypeName(cast))) {\n throw new UnsupportedPredicateError('unsupported-cast');\n }\n}\n\nfunction canonicalInteger(value: string, rank: 0 | 1 | 2): string {\n if (!INTEGER_RE.test(value)) {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n let integer: bigint;\n try {\n integer = BigInt(value);\n } catch {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n const bounds = [\n [-32768n, 32767n],\n [-2147483648n, 2147483647n],\n [-9223372036854775808n, 9223372036854775807n],\n ] as const;\n if (integer < bounds[rank][0] || integer > bounds[rank][1]) {\n throw new UnsupportedPredicateError('unsupported-type');\n }\n return integer.toString();\n}\n\nfunction canonicalTypeName(type: string): string {\n const name = lastTypeName(type).replace(/\\s+/g, ' ');\n switch (name) {\n case 'boolean':\n return 'bool';\n case 'smallint':\n return 'int2';\n case 'integer':\n return 'int4';\n case 'bigint':\n return 'int8';\n case 'character varying':\n return 'varchar';\n default:\n return name;\n }\n}\n\nfunction lastTypeName(type: string): string {\n return type.split('.').at(-1)?.toLowerCase() ?? '';\n}\n\nfunction integerRank(type: string): 0 | 1 | 2 | undefined {\n switch (type) {\n case 'int2':\n return 0;\n case 'int4':\n return 1;\n case 'int8':\n return 2;\n default:\n return undefined;\n }\n}\n\nfunction isComparison(\n type: Token['type'] | undefined,\n): type is '=' | '<>' | '!=' | '<' | '<=' | '>' | '>=' {\n return (\n type === '=' ||\n type === '<>' ||\n type === '!=' ||\n type === '<' ||\n type === '<=' ||\n type === '>' ||\n type === '>='\n );\n}\n\nfunction tokenize(sql: string): readonly Token[] {\n const tokens: Token[] = [];\n for (let i = 0; i < sql.length;) {\n const char = sql[i];\n if (WHITESPACE_RE.test(char)) {\n i++;\n continue;\n }\n if (char === '\"') {\n const result = readQuoted(sql, i, '\"', false);\n tokens.push({type: 'identifier', value: result.value});\n i = result.end;\n continue;\n }\n const escapeString = (char === 'E' || char === 'e') && sql[i + 1] === \"'\";\n if (char === \"'\" || escapeString) {\n const result = readQuoted(\n sql,\n i + (escapeString ? 1 : 0),\n \"'\",\n escapeString,\n );\n tokens.push({\n type: 'literal',\n literalType: 'string',\n value: result.value,\n });\n i = result.end;\n continue;\n }\n const operator = OPERATOR_TOKEN_RE.exec(sql.slice(i))?.[0];\n if (operator) {\n tokens.push({\n type: operator as '::' | '=' | '<>' | '!=' | '<' | '<=' | '>' | '>=',\n });\n i += operator.length;\n continue;\n }\n const unsupportedOperator = UNSUPPORTED_OPERATOR_TOKEN_RE.exec(\n sql.slice(i),\n )?.[0];\n if (unsupportedOperator) {\n tokens.push({type: 'unsupported', value: unsupportedOperator});\n i += unsupportedOperator.length;\n continue;\n }\n if (\n char === '(' ||\n char === ')' ||\n char === '[' ||\n char === ']' ||\n char === ',' ||\n char === '.'\n ) {\n tokens.push({type: char});\n i++;\n continue;\n }\n const integer = INTEGER_TOKEN_RE.exec(sql.slice(i))?.[0];\n if (integer) {\n tokens.push({type: 'literal', literalType: 'integer', value: integer});\n i += integer.length;\n continue;\n }\n const word = WORD_TOKEN_RE.exec(sql.slice(i))?.[0];\n if (word) {\n const keyword = word.toUpperCase();\n if (unsupportedOperatorWords.has(keyword)) {\n throw new UnsupportedPredicateError('unsupported-operator');\n }\n if (keyword === 'TRUE' || keyword === 'FALSE') {\n tokens.push({\n type: 'literal',\n literalType: 'boolean',\n value: keyword === 'TRUE',\n });\n } else if (keyword === 'NULL') {\n tokens.push({type: 'literal', literalType: 'null', value: null});\n } else if (\n keyword === 'AND' ||\n keyword === 'OR' ||\n keyword === 'NOT' ||\n keyword === 'IN' ||\n keyword === 'IS'\n ) {\n tokens.push({type: keyword});\n } else {\n tokens.push({type: 'identifier', value: word.toLowerCase()});\n }\n i += word.length;\n continue;\n }\n tokens.push({type: 'unsupported', value: char});\n i++;\n }\n tokens.push({type: 'EOF'});\n return tokens;\n}\n\nfunction readQuoted(\n sql: string,\n start: number,\n quote: '\"' | \"'\",\n escapes: boolean,\n): {readonly value: string; readonly end: number} {\n let value = '';\n for (let i = start + 1; i < sql.length; i++) {\n const char = sql[i];\n if (char === quote) {\n if (sql[i + 1] === quote) {\n value += quote;\n i++;\n } else {\n return {value, end: i + 1};\n }\n } else if (escapes && char === '\\\\') {\n const escape = sql[++i];\n if (escape === undefined) {\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n if (escape === 'x') {\n const hex = readEscapeDigits(sql, i + 1, 2, 16, 1);\n value += String.fromCharCode(hex.value);\n i = hex.end - 1;\n } else if (escape === 'u' || escape === 'U') {\n const hex = readEscapeDigits(sql, i + 1, escape === 'u' ? 4 : 8, 16);\n try {\n value += String.fromCodePoint(hex.value);\n } catch {\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n i = hex.end - 1;\n } else if (escape >= '0' && escape <= '7') {\n let octal = escape;\n while (\n octal.length < 3 &&\n sql[i + 1] !== undefined &&\n sql[i + 1] >= '0' &&\n sql[i + 1] <= '7'\n ) {\n octal += sql[++i];\n }\n value += String.fromCharCode(Number.parseInt(octal, 8));\n } else if (escape === '\\n') {\n // A backslash-newline pair is a line continuation.\n } else if (escape === '\\r' && sql[i + 1] === '\\n') {\n i++;\n } else {\n value += SIMPLE_ESCAPES[escape] ?? escape;\n }\n } else {\n value += char;\n }\n }\n throw new UnsupportedPredicateError('unsupported-syntax');\n}\n\nfunction readEscapeDigits(\n sql: string,\n start: number,\n length: number,\n radix: 16,\n minLength = length,\n): {readonly value: number; readonly end: number} {\n let end = start;\n while (end < start + length) {\n const digit = sql[end];\n if (\n digit === undefined ||\n !(\n (digit >= '0' && digit <= '9') ||\n (digit.toLowerCase() >= 'a' && digit.toLowerCase() <= 'f')\n )\n ) {\n break;\n }\n end++;\n }\n const digits = sql.slice(start, end);\n if (digits.length < minLength) {\n throw new UnsupportedPredicateError('unsupported-syntax');\n }\n return {value: Number.parseInt(digits, radix), end};\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backup-monitor.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/backup-monitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAKjD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,wBAAwB,CAAC;AACnD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,sBAAsB,CAAC;AAEhE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,qBAAa,aAAc,YAAW,gBAAgB;;IACpD,QAAQ,CAAC,EAAE,oBAAoB;
|
|
1
|
+
{"version":3,"file":"backup-monitor.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/backup-monitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAKjD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,wBAAwB,CAAC;AACnD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,sBAAsB,CAAC;AAEhE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,qBAAa,aAAc,YAAW,gBAAgB;;IACpD,QAAQ,CAAC,EAAE,oBAAoB;IAiB/B,YACE,EAAE,EAAE,UAAU,EACd,UAAU,EAAE,MAAM,CAAC,iBAAiB,CAAC,EACrC,cAAc,EAAE,qBAAqB,EACrC,WAAW,EAAE,MAAM,EAMpB;IAED,mBAAmB,kBAElB;IAEK,GAAG,kBAsBR;IAqDD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAGpB;CA+BF"}
|