@rocicorp/zero 1.12.0-canary.3 → 1.12.0-canary.4
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/_virtual/{_@oxc-project_runtime@0.130.0/helpers → _@oxc-project_runtime@0.154.0/helpers/esm}/usingCtx.js +2 -2
- package/out/analyze-query/src/analyze-cli.js +4 -3
- package/out/analyze-query/src/analyze-cli.js.map +1 -1
- package/out/analyze-query/src/bin-analyze.js +5 -3
- package/out/analyze-query/src/bin-analyze.js.map +1 -1
- package/out/analyze-query/src/bin-transform.js +3 -2
- package/out/analyze-query/src/bin-transform.js.map +1 -1
- package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
- package/out/ast-to-zql/src/bin.js +3 -2
- package/out/ast-to-zql/src/bin.js.map +1 -1
- package/out/ast-to-zql/src/format.js.map +1 -1
- package/out/datadog/src/datadog-log-sink.js +5 -4
- package/out/datadog/src/datadog-log-sink.js.map +1 -1
- package/out/otel/src/log-options.js.map +1 -1
- package/out/replicache/src/btree/node.js +13 -11
- package/out/replicache/src/btree/node.js.map +1 -1
- package/out/replicache/src/btree/read.js +4 -2
- package/out/replicache/src/btree/read.js.map +1 -1
- package/out/replicache/src/btree/splice.js.map +1 -1
- package/out/replicache/src/btree/write.js +9 -5
- package/out/replicache/src/btree/write.js.map +1 -1
- package/out/replicache/src/config.js +1 -2
- package/out/replicache/src/connection-loop.js.map +1 -1
- package/out/replicache/src/dag/chunk.js +1 -1
- package/out/replicache/src/dag/gc.js.map +1 -1
- package/out/replicache/src/dag/lazy-store.js +8 -7
- package/out/replicache/src/dag/lazy-store.js.map +1 -1
- package/out/replicache/src/dag/store-impl.js +1 -1
- package/out/replicache/src/dag/store-impl.js.map +1 -1
- package/out/replicache/src/dag/visitor.js.map +1 -1
- package/out/replicache/src/db/commit.js +5 -3
- package/out/replicache/src/db/commit.js.map +1 -1
- package/out/replicache/src/db/read.js +1 -1
- package/out/replicache/src/db/read.js.map +1 -1
- package/out/replicache/src/db/rebase.js.map +1 -1
- package/out/replicache/src/db/write.js +5 -2
- package/out/replicache/src/db/write.js.map +1 -1
- package/out/replicache/src/deleted-clients.js +3 -2
- package/out/replicache/src/deleted-clients.js.map +1 -1
- package/out/replicache/src/expo-sqlite.js +1 -2
- package/out/replicache/src/frozen-json.js +1 -1
- package/out/replicache/src/frozen-json.js.map +1 -1
- package/out/replicache/src/get-default-puller.js +1 -1
- package/out/replicache/src/hash.js +1 -1
- package/out/replicache/src/hash.js.map +1 -1
- package/out/replicache/src/impl.js +1 -2
- package/out/replicache/src/index-defs.js +5 -3
- package/out/replicache/src/index-defs.js.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.js +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/idb-store.js +1 -1
- package/out/replicache/src/kv/idb-store.js.map +1 -1
- package/out/replicache/src/kv/mem-fallback-store.js +1 -1
- package/out/replicache/src/kv/mem-fallback-store.js.map +1 -1
- package/out/replicache/src/kv/mem-store.js.map +1 -1
- package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/op-sqlite/types.js.map +1 -1
- package/out/replicache/src/kv/read-impl.js.map +1 -1
- package/out/replicache/src/kv/sqlite-store.js +1 -1
- package/out/replicache/src/kv/sqlite-store.js.map +1 -1
- package/out/replicache/src/kv/write-impl-base.js +4 -1
- package/out/replicache/src/kv/write-impl-base.js.map +1 -1
- package/out/replicache/src/kv/write-impl.js.map +1 -1
- package/out/replicache/src/log-options.js +2 -1
- package/out/replicache/src/log-options.js.map +1 -1
- package/out/replicache/src/make-idb-name.js +1 -1
- package/out/replicache/src/new-client-channel.js +9 -7
- package/out/replicache/src/new-client-channel.js.map +1 -1
- package/out/replicache/src/on-persist-channel.js +1 -1
- package/out/replicache/src/on-persist-channel.js.map +1 -1
- package/out/replicache/src/op-sqlite.js +1 -2
- package/out/replicache/src/pending-mutations.js +2 -1
- package/out/replicache/src/pending-mutations.js.map +1 -1
- package/out/replicache/src/persist/client-gc.js +2 -2
- package/out/replicache/src/persist/client-gc.js.map +1 -1
- package/out/replicache/src/persist/client-group-gc.js +2 -2
- package/out/replicache/src/persist/client-group-gc.js.map +1 -1
- package/out/replicache/src/persist/client-groups.js +1 -1
- package/out/replicache/src/persist/client-groups.js.map +1 -1
- package/out/replicache/src/persist/clients.js +1 -1
- package/out/replicache/src/persist/clients.js.map +1 -1
- package/out/replicache/src/persist/collect-idb-databases.js +5 -4
- package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
- package/out/replicache/src/persist/gather-mem-only-visitor.js.map +1 -1
- package/out/replicache/src/persist/gather-not-cached-visitor.js.map +1 -1
- package/out/replicache/src/persist/heartbeat.js +2 -2
- package/out/replicache/src/persist/heartbeat.js.map +1 -1
- package/out/replicache/src/persist/idb-databases-store.js +1 -1
- package/out/replicache/src/persist/idb-databases-store.js.map +1 -1
- package/out/replicache/src/persist/make-client-id.js +1 -1
- package/out/replicache/src/persist/make-client-id.js.map +1 -1
- package/out/replicache/src/persist/persist.js +3 -2
- package/out/replicache/src/persist/persist.js.map +1 -1
- package/out/replicache/src/persist/refresh.js +8 -5
- package/out/replicache/src/persist/refresh.js.map +1 -1
- package/out/replicache/src/process-scheduler.js.map +1 -1
- package/out/replicache/src/replicache-impl.js +3 -3
- package/out/replicache/src/replicache-impl.js.map +1 -1
- package/out/replicache/src/scan-iterator.js.map +1 -1
- package/out/replicache/src/scan-options.js +7 -6
- package/out/replicache/src/scan-options.js.map +1 -1
- package/out/replicache/src/sqlite.js +1 -2
- package/out/replicache/src/subscriptions.js +1 -1
- package/out/replicache/src/subscriptions.js.map +1 -1
- package/out/replicache/src/sync/diff.js +3 -1
- package/out/replicache/src/sync/diff.js.map +1 -1
- package/out/replicache/src/sync/ids.js.map +1 -1
- package/out/replicache/src/sync/patch.js +2 -4
- package/out/replicache/src/sync/patch.js.map +1 -1
- package/out/replicache/src/sync/pull.js +3 -1
- package/out/replicache/src/sync/pull.js.map +1 -1
- package/out/replicache/src/sync/push.js.map +1 -1
- package/out/replicache/src/sync/request-id.js +1 -1
- package/out/replicache/src/sync/request-id.js.map +1 -1
- package/out/replicache/src/transactions.js +1 -1
- package/out/replicache/src/transactions.js.map +1 -1
- package/out/shared/src/arrays.js +4 -1
- package/out/shared/src/arrays.js.map +1 -1
- package/out/shared/src/asserts.js +1 -1
- package/out/shared/src/bigint-json.d.ts +9 -0
- package/out/shared/src/bigint-json.d.ts.map +1 -1
- package/out/shared/src/bigint-json.js +27 -2
- package/out/shared/src/bigint-json.js.map +1 -1
- package/out/shared/src/broadcast-channel.js +1 -1
- package/out/shared/src/btree-set.js +11 -8
- package/out/shared/src/btree-set.js.map +1 -1
- package/out/shared/src/cache.js.map +1 -1
- package/out/shared/src/config.js +1 -1
- package/out/shared/src/custom-key-map.js.map +1 -1
- package/out/shared/src/custom-key-set.js.map +1 -1
- package/out/shared/src/deep-clone.js +1 -1
- package/out/shared/src/document-visible.js.map +1 -1
- package/out/shared/src/dotenv.js.map +1 -1
- package/out/shared/src/error.js +1 -1
- package/out/shared/src/error.js.map +1 -1
- package/out/shared/src/hash.js +2 -2
- package/out/shared/src/hash.js.map +1 -1
- package/out/shared/src/iterables.js.map +1 -1
- package/out/shared/src/json-schema.js.map +1 -1
- package/out/shared/src/json.js.map +1 -1
- package/out/shared/src/logging-test-utils.js +1 -1
- package/out/shared/src/logging.js +1 -1
- package/out/shared/src/logging.js.map +1 -1
- package/out/shared/src/navigator.js +1 -1
- package/out/shared/src/options.js +9 -12
- package/out/shared/src/options.js.map +1 -1
- package/out/shared/src/promise-race.js.map +1 -1
- package/out/shared/src/queue.js.map +1 -1
- package/out/shared/src/record-proxy.js.map +1 -1
- package/out/shared/src/ring-buffer.js.map +1 -1
- package/out/shared/src/size-of-value.js +7 -10
- package/out/shared/src/size-of-value.js.map +1 -1
- package/out/shared/src/sorted-entries.js.map +1 -1
- package/out/shared/src/tdigest-schema.js.map +1 -1
- package/out/shared/src/tdigest.js.map +1 -1
- package/out/shared/src/valita.js +1 -1
- package/out/shared/src/valita.js.map +1 -1
- package/out/z2s/src/compiler.js +2 -4
- package/out/z2s/src/compiler.js.map +1 -1
- package/out/z2s/src/sql.js +1 -1
- package/out/z2s/src/sql.js.map +1 -1
- package/out/zero/package.js +212 -189
- package/out/zero/package.js.map +1 -1
- package/out/zero/src/adapters/drizzle.js +1 -2
- package/out/zero/src/adapters/kysely.js +1 -2
- package/out/zero/src/adapters/pg.js +1 -2
- package/out/zero/src/adapters/postgresjs.js +1 -2
- package/out/zero/src/adapters/prisma.js +1 -2
- package/out/zero/src/analyze.js +1 -2
- package/out/zero/src/change-protocol/v0.js +1 -2
- package/out/zero/src/expo-sqlite.js +1 -2
- package/out/zero/src/op-sqlite.js +1 -2
- package/out/zero/src/react-native.js.map +1 -1
- package/out/zero/src/sqlite.js +1 -2
- package/out/zero/src/zero-cache-dev.js +10 -9
- package/out/zero/src/zero-cache-dev.js.map +1 -1
- package/out/zero/src/zero-out.js +2 -1
- package/out/zero/src/zero-out.js.map +1 -1
- package/out/zero-cache/src/auth/auth.js +1 -1
- package/out/zero-cache/src/auth/auth.js.map +1 -1
- package/out/zero-cache/src/auth/jwt.js +4 -1
- package/out/zero-cache/src/auth/jwt.js.map +1 -1
- package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
- package/out/zero-cache/src/auth/read-authorizer.js +3 -2
- package/out/zero-cache/src/auth/read-authorizer.js.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.js +5 -16
- package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
- package/out/zero-cache/src/config/network.js +1 -1
- package/out/zero-cache/src/config/network.js.map +1 -1
- package/out/zero-cache/src/config/server-context.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +5 -0
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +13 -2
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/custom/fetch.js +1 -1
- package/out/zero-cache/src/custom/fetch.js.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
- package/out/zero-cache/src/db/create.js.map +1 -1
- package/out/zero-cache/src/db/index-predicate.js.map +1 -1
- package/out/zero-cache/src/db/lite-tables.d.ts +2 -2
- package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
- package/out/zero-cache/src/db/lite-tables.js.map +1 -1
- package/out/zero-cache/src/db/migration-lite.js +1 -1
- package/out/zero-cache/src/db/migration-lite.js.map +1 -1
- package/out/zero-cache/src/db/migration.js +5 -3
- package/out/zero-cache/src/db/migration.js.map +1 -1
- package/out/zero-cache/src/db/pg-copy-binary.js +4 -3
- package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -1
- package/out/zero-cache/src/db/pg-copy.js +0 -1
- package/out/zero-cache/src/db/pg-copy.js.map +1 -1
- package/out/zero-cache/src/db/pg-to-lite.js +1 -1
- package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
- package/out/zero-cache/src/db/run-transaction.js.map +1 -1
- package/out/zero-cache/src/db/specs.js +7 -5
- package/out/zero-cache/src/db/specs.js.map +1 -1
- package/out/zero-cache/src/db/sqlite-corruption.js.map +1 -1
- package/out/zero-cache/src/db/statements.d.ts.map +1 -1
- package/out/zero-cache/src/db/statements.js +9 -2
- package/out/zero-cache/src/db/statements.js.map +1 -1
- package/out/zero-cache/src/db/transaction-pool.js +5 -3
- package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
- package/out/zero-cache/src/db/warmup.js.map +1 -1
- package/out/zero-cache/src/observability/events.js.map +1 -1
- package/out/zero-cache/src/observability/log-throttle.js.map +1 -1
- package/out/zero-cache/src/observability/metrics.js.map +1 -1
- package/out/zero-cache/src/scripts/deploy-permissions.js +4 -2
- package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
- package/out/zero-cache/src/scripts/permissions.js.map +1 -1
- package/out/zero-cache/src/server/anonymous-otel-start.js +2 -1
- package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
- package/out/zero-cache/src/server/logging.js +1 -1
- package/out/zero-cache/src/server/logging.js.map +1 -1
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
- package/out/zero-cache/src/server/otel-start.js.map +1 -1
- package/out/zero-cache/src/server/reaper.js +2 -2
- package/out/zero-cache/src/server/reaper.js.map +1 -1
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/server/runner/run-worker.js +2 -1
- package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
- package/out/zero-cache/src/server/runner/runtime.js +2 -1
- package/out/zero-cache/src/server/runner/runtime.js.map +1 -1
- package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
- package/out/zero-cache/src/server/shadow-syncer.js +1 -1
- package/out/zero-cache/src/server/shadow-syncer.js.map +1 -1
- package/out/zero-cache/src/server/slot-keeper.js.map +1 -1
- package/out/zero-cache/src/server/syncer-assigner.js.map +1 -1
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +6 -1
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/server/worker-dispatcher.js +1 -1
- package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
- package/out/zero-cache/src/server/worker-urls.js.map +1 -1
- package/out/zero-cache/src/services/analyze.js +1 -1
- package/out/zero-cache/src/services/analyze.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js +1 -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.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/init-cleanup.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-restore.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.js +3 -3
- package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +3 -3
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-metadata.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +3 -3
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source-init.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source-init.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js +4 -6
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/decommission.js +2 -1
- package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js +5 -3
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +3 -2
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/lsn.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js +2 -2
- package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slot-health.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slot-health.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slots.js +3 -3
- package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +13 -12
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/published.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js +9 -6
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/validation.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js +1 -1
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/control.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current.js +1 -1
- package/out/zero-cache/src/services/change-source/protocol/mod.js +1 -2
- package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backfill-state.js +1 -1
- package/out/zero-cache/src/services/change-streamer/backfill-state.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/broadcast.js +1 -1
- package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts +1 -1
- 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 +2 -3
- package/out/zero-cache/src/services/change-streamer/change-log-codec.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +2 -2
- package/out/zero-cache/src/services/change-streamer/change-log-initializer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +5 -3
- 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.js +21 -21
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/drain-tracker.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/forwarder.js +11 -10
- package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js +1 -1
- package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js +3 -3
- package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/replica-poller.js +1 -1
- package/out/zero-cache/src/services/change-streamer/replica-poller.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/restore-admission.js +12 -1
- package/out/zero-cache/src/services/change-streamer/restore-admission.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/init.js +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.js +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-message.js +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-message.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js +2 -2
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js +4 -6
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +1 -3
- 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.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js +2 -2
- package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscribe.js +1 -1
- package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js +2 -2
- package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/upstream-acker.js +0 -1
- package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -1
- package/out/zero-cache/src/services/heapz.js +2 -1
- package/out/zero-cache/src/services/heapz.js.map +1 -1
- package/out/zero-cache/src/services/http-service.js.map +1 -1
- package/out/zero-cache/src/services/life-cycle.js +1 -1
- package/out/zero-cache/src/services/life-cycle.js.map +1 -1
- package/out/zero-cache/src/services/limiter/sliding-window-limiter.js +4 -2
- package/out/zero-cache/src/services/limiter/sliding-window-limiter.js.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js +1 -1
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +2 -2
- package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +1 -1
- package/out/zero-cache/src/services/litestream/litestream-controller.js.map +1 -1
- package/out/zero-cache/src/services/litestream/metrics.js.map +1 -1
- package/out/zero-cache/src/services/litestream/restore-progress.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js +3 -2
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/plannerz.js +4 -3
- package/out/zero-cache/src/services/plannerz.js.map +1 -1
- package/out/zero-cache/src/services/profz.js +5 -3
- package/out/zero-cache/src/services/profz.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-log-db.js +3 -2
- package/out/zero-cache/src/services/replicator/change-log-db.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +1 -1
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
- package/out/zero-cache/src/services/replicator/drain-handler.js.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
- package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replication-status.js +4 -3
- package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/recorder.js +1 -1
- package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/report-schema.js +1 -1
- package/out/zero-cache/src/services/replicator/reporter/report-schema.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/backfilling.js +1 -1
- package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/change-log.d.ts +19 -7
- package/out/zero-cache/src/services/replicator/schema/change-log.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/change-log.js +31 -15
- package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/column-metadata.js +1 -1
- package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/replication-state.js +10 -7
- package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/table-metadata.js.map +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.js +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
- package/out/zero-cache/src/services/run-ast.js.map +1 -1
- package/out/zero-cache/src/services/runner.js.map +1 -1
- package/out/zero-cache/src/services/running-state.js.map +1 -1
- package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
- package/out/zero-cache/src/services/statz.js +2 -1
- package/out/zero-cache/src/services/statz.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/active-users-gauge.js +2 -2
- package/out/zero-cache/src/services/view-syncer/active-users-gauge.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/advancement-metrics.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/batch-pacer.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js +24 -3
- package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-schema.d.ts +15 -2
- package/out/zero-cache/src/services/view-syncer/client-schema.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-schema.js +46 -6
- package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-purger.js +2 -2
- package/out/zero-cache/src/services/view-syncer/cvr-purger.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.js +10 -5
- package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +12 -4
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js +1 -1
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/hydration-budget.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js +2 -2
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js +5 -3
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
- 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 +8 -5
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/plan-warnings.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/push-cost.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/push-cutoff.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-shape.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-stats.js +1 -1
- package/out/zero-cache/src/services/view-syncer/query-stats.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/cvr.js +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/types.js +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js +7 -1
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +8 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js +6 -5
- package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/ttl-clock.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 +13 -9
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/error-with-level.js.map +1 -1
- package/out/zero-cache/src/types/lexi-version.js.map +1 -1
- package/out/zero-cache/src/types/lite.js +1 -1
- package/out/zero-cache/src/types/lite.js.map +1 -1
- package/out/zero-cache/src/types/pg-data-type.js +1 -1
- package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
- package/out/zero-cache/src/types/pg.js +6 -4
- package/out/zero-cache/src/types/pg.js.map +1 -1
- package/out/zero-cache/src/types/poke-chunk.js +2 -2
- package/out/zero-cache/src/types/poke-chunk.js.map +1 -1
- package/out/zero-cache/src/types/processes.js +1 -1
- package/out/zero-cache/src/types/processes.js.map +1 -1
- package/out/zero-cache/src/types/profiler.js.map +1 -1
- package/out/zero-cache/src/types/row-key.js.map +1 -1
- package/out/zero-cache/src/types/sql.js.map +1 -1
- package/out/zero-cache/src/types/streams.js +10 -9
- package/out/zero-cache/src/types/streams.js.map +1 -1
- package/out/zero-cache/src/types/strings.js.map +1 -1
- package/out/zero-cache/src/types/subscription.js.map +1 -1
- package/out/zero-cache/src/types/timeout.js +1 -1
- package/out/zero-cache/src/types/websocket-handoff.js +1 -1
- package/out/zero-cache/src/types/ws.js +1 -1
- package/out/zero-cache/src/workers/connection.js +5 -6
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/mutator.js.map +1 -1
- package/out/zero-cache/src/workers/replicator.js +5 -4
- package/out/zero-cache/src/workers/replicator.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 +9 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +4 -4
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/active-clients-manager.js +1 -1
- package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
- package/out/zero-client/src/client/bindings.js +11 -11
- package/out/zero-client/src/client/connection-manager.js.map +1 -1
- package/out/zero-client/src/client/connection.js +1 -1
- package/out/zero-client/src/client/connection.js.map +1 -1
- package/out/zero-client/src/client/context.js.map +1 -1
- package/out/zero-client/src/client/crud-impl.js +1 -1
- package/out/zero-client/src/client/crud-impl.js.map +1 -1
- package/out/zero-client/src/client/crud.js +1 -1
- package/out/zero-client/src/client/crud.js.map +1 -1
- package/out/zero-client/src/client/custom.js +1 -1
- package/out/zero-client/src/client/custom.js.map +1 -1
- package/out/zero-client/src/client/delete-clients-manager.js.map +1 -1
- package/out/zero-client/src/client/enable-analytics.js +1 -1
- package/out/zero-client/src/client/enable-analytics.js.map +1 -1
- package/out/zero-client/src/client/error.js.map +1 -1
- package/out/zero-client/src/client/inspector/client-group.js.map +1 -1
- package/out/zero-client/src/client/inspector/client.js.map +1 -1
- package/out/zero-client/src/client/inspector/html-dialog-prompt.js +1 -1
- package/out/zero-client/src/client/inspector/html-dialog-prompt.js.map +1 -1
- package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
- package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
- package/out/zero-client/src/client/inspector/query.js.map +1 -1
- package/out/zero-client/src/client/ivm-branch.js +1 -3
- package/out/zero-client/src/client/ivm-branch.js.map +1 -1
- package/out/zero-client/src/client/keys.js +2 -1
- package/out/zero-client/src/client/keys.js.map +1 -1
- package/out/zero-client/src/client/log-options.js.map +1 -1
- package/out/zero-client/src/client/make-replicache-mutators.js +5 -3
- package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
- package/out/zero-client/src/client/metrics.js +3 -5
- package/out/zero-client/src/client/metrics.js.map +1 -1
- package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
- package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
- package/out/zero-client/src/client/options.js.map +1 -1
- package/out/zero-client/src/client/query-manager.js +2 -2
- package/out/zero-client/src/client/query-manager.js.map +1 -1
- package/out/zero-client/src/client/reload-error-handler.js +4 -3
- package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero-poke-handler.js +1 -1
- package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
- package/out/zero-client/src/client/zero-rep.js.map +1 -1
- package/out/zero-client/src/client/zero.js +19 -11
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-client/src/mod.js +23 -23
- package/out/zero-client/src/util/nanoid.js.map +1 -1
- package/out/zero-permissions/src/permissions.js +3 -2
- package/out/zero-permissions/src/permissions.js.map +1 -1
- package/out/zero-pg/src/mod.js +9 -9
- package/out/zero-protocol/src/analyze-query-result.js +1 -1
- package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
- package/out/zero-protocol/src/application-error.js.map +1 -1
- package/out/zero-protocol/src/ast.js +19 -9
- package/out/zero-protocol/src/ast.js.map +1 -1
- package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
- package/out/zero-protocol/src/client-schema.js +1 -1
- package/out/zero-protocol/src/client-schema.js.map +1 -1
- package/out/zero-protocol/src/close-connection.js +1 -1
- package/out/zero-protocol/src/close-connection.js.map +1 -1
- package/out/zero-protocol/src/connect.js +1 -1
- package/out/zero-protocol/src/connect.js.map +1 -1
- package/out/zero-protocol/src/custom-queries.js +1 -1
- package/out/zero-protocol/src/custom-queries.js.map +1 -1
- package/out/zero-protocol/src/data.js +4 -2
- package/out/zero-protocol/src/data.js.map +1 -1
- package/out/zero-protocol/src/delete-clients.js.map +1 -1
- package/out/zero-protocol/src/down.d.ts +27 -27
- package/out/zero-protocol/src/down.js.map +1 -1
- package/out/zero-protocol/src/error.js.map +1 -1
- package/out/zero-protocol/src/inspect-down.d.ts +81 -81
- package/out/zero-protocol/src/inspect-down.js +1 -1
- package/out/zero-protocol/src/inspect-down.js.map +1 -1
- package/out/zero-protocol/src/inspect-up.js +1 -1
- package/out/zero-protocol/src/inspect-up.js.map +1 -1
- package/out/zero-protocol/src/mutate-server.js +1 -1
- package/out/zero-protocol/src/mutate-server.js.map +1 -1
- package/out/zero-protocol/src/mutation-id.js.map +1 -1
- package/out/zero-protocol/src/mutation.js +1 -1
- package/out/zero-protocol/src/mutation.js.map +1 -1
- package/out/zero-protocol/src/mutations-patch.js +1 -1
- package/out/zero-protocol/src/mutations-patch.js.map +1 -1
- package/out/zero-protocol/src/ping.js +1 -1
- package/out/zero-protocol/src/ping.js.map +1 -1
- package/out/zero-protocol/src/poke.d.ts +6 -6
- package/out/zero-protocol/src/poke.js +1 -1
- package/out/zero-protocol/src/poke.js.map +1 -1
- package/out/zero-protocol/src/pong.js +1 -1
- package/out/zero-protocol/src/pong.js.map +1 -1
- package/out/zero-protocol/src/primary-key.js.map +1 -1
- package/out/zero-protocol/src/protocol-version.js +1 -0
- package/out/zero-protocol/src/protocol-version.js.map +1 -1
- package/out/zero-protocol/src/pull.js +1 -1
- package/out/zero-protocol/src/pull.js.map +1 -1
- package/out/zero-protocol/src/push.js +1 -1
- package/out/zero-protocol/src/push.js.map +1 -1
- package/out/zero-protocol/src/queries-patch.js +1 -1
- package/out/zero-protocol/src/queries-patch.js.map +1 -1
- package/out/zero-protocol/src/query-hash-visitor.js.map +1 -1
- package/out/zero-protocol/src/query-hash.js +2 -2
- package/out/zero-protocol/src/query-server.js +1 -1
- package/out/zero-protocol/src/query-server.js.map +1 -1
- package/out/zero-protocol/src/row-patch.js.map +1 -1
- package/out/zero-protocol/src/up.d.ts +60 -60
- package/out/zero-protocol/src/up.js.map +1 -1
- package/out/zero-protocol/src/update-auth.js.map +1 -1
- package/out/zero-protocol/src/version.js.map +1 -1
- package/out/zero-react/src/bindings.js +11 -11
- package/out/zero-react/src/mod.js +5 -5
- package/out/zero-react/src/use-query.js +1 -1
- package/out/zero-react/src/use-query.js.map +1 -1
- package/out/zero-react/src/zero-provider.d.ts +1 -1
- package/out/zero-react/src/zero-provider.d.ts.map +1 -1
- package/out/zero-react/src/zero-provider.js +2 -1
- package/out/zero-react/src/zero-provider.js.map +1 -1
- package/out/zero-react/src/zero.js +23 -23
- package/out/zero-schema/src/builder/schema-builder.js +3 -2
- package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
- package/out/zero-schema/src/builder/table-builder.js +1 -1
- package/out/zero-schema/src/builder/table-builder.js.map +1 -1
- package/out/zero-schema/src/compiled-permissions.js.map +1 -1
- package/out/zero-schema/src/name-mapper.js +7 -5
- package/out/zero-schema/src/name-mapper.js.map +1 -1
- package/out/zero-schema/src/schema-config.js +1 -1
- package/out/zero-schema/src/schema-config.js.map +1 -1
- package/out/zero-server/src/adapters/drizzle.js.map +1 -1
- package/out/zero-server/src/adapters/kysely.js.map +1 -1
- package/out/zero-server/src/adapters/pg.js.map +1 -1
- package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
- package/out/zero-server/src/adapters/prisma.js.map +1 -1
- package/out/zero-server/src/closable-db-transaction.js.map +1 -1
- package/out/zero-server/src/custom.js +1 -1
- package/out/zero-server/src/custom.js.map +1 -1
- package/out/zero-server/src/mod.js +8 -8
- package/out/zero-server/src/process-mutations.js.map +1 -1
- package/out/zero-server/src/push-processor.js.map +1 -1
- package/out/zero-server/src/queries/process-queries.js +2 -1
- package/out/zero-server/src/queries/process-queries.js.map +1 -1
- package/out/zero-server/src/schema.js +7 -7
- package/out/zero-server/src/schema.js.map +1 -1
- package/out/zero-server/src/zql-database.js.map +1 -1
- package/out/zero-solid/src/bindings.js +11 -11
- package/out/zero-solid/src/mod.js +5 -5
- package/out/zero-solid/src/solid-view.js +1 -1
- package/out/zero-solid/src/solid-view.js.map +1 -1
- package/out/zero-solid/src/use-connection-state.js +2 -1
- package/out/zero-solid/src/use-connection-state.js.map +1 -1
- package/out/zero-solid/src/use-query.js.map +1 -1
- package/out/zero-solid/src/use-zero-online.js +2 -1
- package/out/zero-solid/src/use-zero-online.js.map +1 -1
- package/out/zero-solid/src/use-zero.js +3 -2
- package/out/zero-solid/src/use-zero.js.map +1 -1
- package/out/zero-solid/src/zero.js +23 -23
- package/out/zero-types/src/name-mapper.js.map +1 -1
- package/out/zql/src/builder/builder.js +15 -13
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/builder/correlated-predicate-pushdown.js.map +1 -1
- package/out/zql/src/builder/debug-delegate.js.map +1 -1
- package/out/zql/src/builder/like.js +0 -1
- package/out/zql/src/builder/like.js.map +1 -1
- package/out/zql/src/ivm/array-view.js.map +1 -1
- package/out/zql/src/ivm/cap.js.map +1 -1
- package/out/zql/src/ivm/constraint.js +1 -1
- package/out/zql/src/ivm/constraint.js.map +1 -1
- package/out/zql/src/ivm/data.js.map +1 -1
- package/out/zql/src/ivm/deferred-input.js.map +1 -1
- package/out/zql/src/ivm/exists.js +20 -18
- package/out/zql/src/ivm/exists.js.map +1 -1
- package/out/zql/src/ivm/fan-in.js.map +1 -1
- package/out/zql/src/ivm/fan-out.js.map +1 -1
- package/out/zql/src/ivm/filter-operators.js +1 -1
- package/out/zql/src/ivm/filter-operators.js.map +1 -1
- package/out/zql/src/ivm/filter-push.js.map +1 -1
- package/out/zql/src/ivm/filter.js.map +1 -1
- package/out/zql/src/ivm/flipped-join.js +1 -3
- package/out/zql/src/ivm/flipped-join.js.map +1 -1
- package/out/zql/src/ivm/join-index-tap.js.map +1 -1
- package/out/zql/src/ivm/join-index.js.map +1 -1
- package/out/zql/src/ivm/join-utils.js +11 -13
- package/out/zql/src/ivm/join-utils.js.map +1 -1
- package/out/zql/src/ivm/join.js.map +1 -1
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
- package/out/zql/src/ivm/memory-source.js +26 -22
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/ivm/memory-storage.js.map +1 -1
- package/out/zql/src/ivm/operator.js +2 -2
- package/out/zql/src/ivm/push-accumulated.js +0 -1
- package/out/zql/src/ivm/push-accumulated.js.map +1 -1
- package/out/zql/src/ivm/skip-yields.js.map +1 -1
- package/out/zql/src/ivm/skip.js.map +1 -1
- package/out/zql/src/ivm/take-gate.js.map +1 -1
- package/out/zql/src/ivm/take.js +1 -1
- package/out/zql/src/ivm/take.js.map +1 -1
- package/out/zql/src/ivm/union-fan-in.js +1 -1
- package/out/zql/src/ivm/union-fan-in.js.map +1 -1
- package/out/zql/src/ivm/union-fan-out.js.map +1 -1
- package/out/zql/src/ivm/view-apply-change.js +1 -1
- package/out/zql/src/ivm/view-apply-change.js.map +1 -1
- package/out/zql/src/mutate/crud.js +3 -2
- package/out/zql/src/mutate/crud.js.map +1 -1
- package/out/zql/src/planner/planner-builder.js +1 -1
- package/out/zql/src/planner/planner-builder.js.map +1 -1
- package/out/zql/src/planner/planner-connection.js.map +1 -1
- package/out/zql/src/planner/planner-debug.js.map +1 -1
- package/out/zql/src/planner/planner-fan-in.js.map +1 -1
- package/out/zql/src/planner/planner-fan-out.js.map +1 -1
- package/out/zql/src/planner/planner-filter.js.map +1 -1
- package/out/zql/src/planner/planner-graph.js +1 -5
- package/out/zql/src/planner/planner-graph.js.map +1 -1
- package/out/zql/src/planner/planner-join.js +1 -1
- package/out/zql/src/planner/planner-join.js.map +1 -1
- package/out/zql/src/planner/planner-source.js.map +1 -1
- package/out/zql/src/planner/planner-terminus.js.map +1 -1
- package/out/zql/src/query/create-builder.js +2 -1
- package/out/zql/src/query/create-builder.js.map +1 -1
- package/out/zql/src/query/expression.js +1 -1
- package/out/zql/src/query/expression.js.map +1 -1
- package/out/zql/src/query/measure-push-operator.js.map +1 -1
- package/out/zql/src/query/named.js +2 -1
- package/out/zql/src/query/named.js.map +1 -1
- package/out/zql/src/query/query-delegate-base.js +1 -1
- package/out/zql/src/query/query-delegate-base.js.map +1 -1
- package/out/zql/src/query/query-impl.js +3 -2
- package/out/zql/src/query/query-impl.js.map +1 -1
- package/out/zql/src/query/query-registry.js +1 -1
- package/out/zql/src/query/query-registry.js.map +1 -1
- package/out/zql/src/query/query-transitions.js +1 -1
- package/out/zql/src/query/query-transitions.js.map +1 -1
- package/out/zql/src/query/runnable-query-impl.js +3 -2
- package/out/zql/src/query/runnable-query-impl.js.map +1 -1
- package/out/zql/src/query/static-query.js.map +1 -1
- package/out/zql/src/query/ttl.js +8 -8
- package/out/zql/src/query/ttl.js.map +1 -1
- package/out/zql/src/query/validate-input.js.map +1 -1
- package/out/zqlite/src/database-storage.d.ts +27 -8
- package/out/zqlite/src/database-storage.d.ts.map +1 -1
- package/out/zqlite/src/database-storage.js +190 -53
- package/out/zqlite/src/database-storage.js.map +1 -1
- package/out/zqlite/src/db.d.ts +47 -1
- package/out/zqlite/src/db.d.ts.map +1 -1
- package/out/zqlite/src/db.js +121 -21
- package/out/zqlite/src/db.js.map +1 -1
- package/out/zqlite/src/explain-queries.js.map +1 -1
- package/out/zqlite/src/fetch-sql.js.map +1 -1
- package/out/zqlite/src/index-usage.js.map +1 -1
- package/out/zqlite/src/internal/sql-inline.js.map +1 -1
- package/out/zqlite/src/internal/sql.js.map +1 -1
- package/out/zqlite/src/internal/statement-cache.d.ts +7 -0
- package/out/zqlite/src/internal/statement-cache.d.ts.map +1 -1
- package/out/zqlite/src/internal/statement-cache.js +11 -4
- package/out/zqlite/src/internal/statement-cache.js.map +1 -1
- package/out/zqlite/src/mod.js +5 -5
- package/out/zqlite/src/pending-delta.js.map +1 -1
- package/out/zqlite/src/query-builder.js +1 -1
- package/out/zqlite/src/query-builder.js.map +1 -1
- package/out/zqlite/src/query-delegate.js.map +1 -1
- package/out/zqlite/src/resolve-scalar-subqueries.js +2 -5
- package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.js +3 -2
- package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
- package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
- package/out/zqlite/src/table-source.d.ts +11 -0
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +62 -25
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +17 -15
- package/out/otel/src/maybe-time.d.ts +0 -3
- package/out/otel/src/maybe-time.d.ts.map +0 -1
- package/out/otel/src/maybe-time.js +0 -15
- package/out/otel/src/maybe-time.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-fan-in.js","names":[
|
|
1
|
+
{"version":3,"file":"planner-fan-in.js","names":[],"sources":["../../../../../zql/src/planner/planner-fan-in.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {PlannerConstraint} from './planner-constraint.ts';\nimport type {PlanDebugger} from './planner-debug.ts';\nimport {omitFanout} from './planner-node.ts';\nimport type {\n CostEstimate,\n JoinOrConnection,\n PlannerNode,\n} from './planner-node.ts';\nimport type {PlannerTerminus} from './planner-terminus.ts';\n\n/**\n * A PlannerFanIn node can either be a normal FanIn or UnionFanIn.\n *\n * These have different performance characteristics so we need to distinguish them.\n *\n * A normal FanIn only does a single fetch to FanOut, regardless of how many internal\n * branches / inputs it has.\n *\n * A UnionFanIn does a fetch per internal branch / input. This causes an exponential\n * increase in cost if many UnionFanIns are chained after on another. E.g., `(A or B) AND (C or D)`.\n *\n * To capture this cost blow-up, union fan in assigns different branch patterns to their inputs.\n *\n * Since UFI will generate a unique branch pattern per input, planner-connection will yield a higher cost\n * each time a UFI is present. planner-connection will return the sum of the costs of each unique branch pattern.\n */\nexport class PlannerFanIn {\n readonly kind = 'fan-in' as const;\n #type: 'FI' | 'UFI';\n #output?: PlannerNode | undefined;\n readonly #inputs: Exclude<PlannerNode, PlannerTerminus>[];\n\n constructor(inputs: Exclude<PlannerNode, PlannerTerminus>[]) {\n this.#type = 'FI';\n this.#inputs = inputs;\n }\n\n get type() {\n return this.#type;\n }\n\n closestJoinOrSource(): JoinOrConnection {\n return 'join';\n }\n\n setOutput(node: PlannerNode): void {\n this.#output = node;\n }\n\n get output(): PlannerNode {\n assert(this.#output !== undefined, 'Output not set');\n return this.#output;\n }\n\n reset() {\n this.#type = 'FI';\n }\n\n convertToUFI(): void {\n this.#type = 'UFI';\n }\n\n /**\n * Propagate unlimiting when a parent join is flipped.\n * Fan-in propagates to all of its inputs.\n */\n propagateUnlimitFromFlippedJoin(): void {\n for (const input of this.#inputs) {\n if (\n 'propagateUnlimitFromFlippedJoin' in input &&\n typeof input.propagateUnlimitFromFlippedJoin === 'function'\n ) {\n (\n input as {propagateUnlimitFromFlippedJoin(): void}\n ).propagateUnlimitFromFlippedJoin();\n }\n }\n }\n\n estimateCost(\n downstreamChildSelectivity: number,\n branchPattern: number[],\n planDebugger?: PlanDebugger,\n ): CostEstimate {\n // FanIn always sums costs of its inputs\n // But it needs to pass the correct branch pattern to each input\n let totalCost: CostEstimate = {\n returnedRows: 0,\n cost: 0,\n scanEst: 0,\n startupCost: 0,\n selectivity: 0,\n limit: undefined,\n fanout: () => {\n throw new Error('Failed to set fanout model');\n },\n };\n\n if (this.#type === 'FI') {\n // Normal FanIn: all inputs get the same branch pattern with 0 prepended\n const updatedPattern = [0, ...branchPattern];\n let maxrows = 0;\n let maxRunningCost = 0;\n let maxStartupCost = 0;\n let maxScanEst = 0;\n\n let noMatchProb = 1.0;\n for (const input of this.#inputs) {\n const cost = input.estimateCost(\n downstreamChildSelectivity,\n updatedPattern,\n planDebugger,\n );\n totalCost.fanout = cost.fanout;\n if (cost.returnedRows > maxrows) {\n maxrows = cost.returnedRows;\n }\n if (cost.cost > maxRunningCost) {\n maxRunningCost = cost.cost;\n }\n if (cost.startupCost > maxStartupCost) {\n maxStartupCost = cost.startupCost;\n }\n if (cost.scanEst > maxScanEst) {\n maxScanEst = cost.scanEst;\n }\n\n // OR branches: combine selectivities assuming independent events\n // P(A OR B) = 1 - (1-A)(1-B)\n // Track probability of NO match in any branch\n noMatchProb *= 1 - cost.selectivity;\n\n // all inputs should have the same limit.\n assert(\n totalCost.limit === undefined || cost.limit === totalCost.limit,\n 'All FanIn inputs should have the same limit',\n );\n totalCost.limit = cost.limit;\n }\n\n totalCost.returnedRows = maxrows;\n totalCost.cost = maxRunningCost;\n totalCost.selectivity = 1 - noMatchProb;\n totalCost.startupCost = maxStartupCost;\n totalCost.scanEst = maxScanEst;\n } else {\n // Union FanIn (UFI): each input gets unique branch pattern\n let i = 0;\n\n let noMatchProb = 1.0;\n for (const input of this.#inputs) {\n const updatedPattern = [i, ...branchPattern];\n const cost = input.estimateCost(\n downstreamChildSelectivity,\n updatedPattern,\n planDebugger,\n );\n totalCost.fanout = cost.fanout;\n totalCost.returnedRows += cost.returnedRows;\n totalCost.cost += cost.cost;\n totalCost.scanEst += cost.scanEst;\n totalCost.startupCost = totalCost.startupCost + cost.startupCost;\n\n // OR branches: combine selectivities assuming independent events\n // P(A OR B) = 1 - (1-A)(1-B)\n // Track probability of NO match in any branch\n noMatchProb *= 1 - cost.selectivity;\n\n // all inputs should have the same limit.\n assert(\n totalCost.limit === undefined || cost.limit === totalCost.limit,\n 'All FanIn inputs should have the same limit',\n );\n totalCost.limit = cost.limit;\n i++;\n }\n totalCost.selectivity = 1 - noMatchProb;\n }\n\n if (planDebugger) {\n planDebugger.log({\n type: 'node-cost',\n nodeType: 'fan-in',\n node: this.#type,\n branchPattern,\n downstreamChildSelectivity,\n costEstimate: omitFanout(totalCost),\n });\n }\n\n return totalCost;\n }\n\n propagateConstraints(\n branchPattern: number[],\n constraint: PlannerConstraint | undefined,\n from?: PlannerNode,\n planDebugger?: PlanDebugger,\n ): void {\n planDebugger?.log({\n type: 'node-constraint',\n nodeType: 'fan-in',\n node: this.#type,\n branchPattern,\n constraint,\n from: from?.kind ?? 'unknown',\n });\n\n if (this.#type === 'FI') {\n const updatedPattern = [0, ...branchPattern];\n /**\n * All inputs get the same branch pattern.\n * 1. They cannot contribute differing constraints to their parent inputs because they are not flipped.\n * If they were flipped this would be of type UFI.\n * 2. All inputs need to be called because they could be pinned. If they are pinned they could have constraints\n * to send to their children.\n */\n for (const input of this.#inputs) {\n input.propagateConstraints(\n updatedPattern,\n constraint,\n this,\n planDebugger,\n );\n }\n return;\n }\n\n let i = 0;\n for (const input of this.#inputs) {\n input.propagateConstraints(\n [i, ...branchPattern],\n constraint,\n this,\n planDebugger,\n );\n i++;\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-fan-out.js","names":[
|
|
1
|
+
{"version":3,"file":"planner-fan-out.js","names":[],"sources":["../../../../../zql/src/planner/planner-fan-out.ts"],"sourcesContent":["import type {PlannerConstraint} from './planner-constraint.ts';\nimport type {PlanDebugger} from './planner-debug.ts';\nimport {omitFanout} from './planner-node.ts';\nimport type {\n CostEstimate,\n JoinOrConnection,\n PlannerNode,\n} from './planner-node.ts';\nimport type {PlannerTerminus} from './planner-terminus.ts';\n\nexport class PlannerFanOut {\n readonly kind = 'fan-out' as const;\n #type: 'FO' | 'UFO';\n readonly #outputs: PlannerNode[] = [];\n readonly #input: Exclude<PlannerNode, PlannerTerminus>;\n\n constructor(input: Exclude<PlannerNode, PlannerTerminus>) {\n this.#type = 'FO';\n this.#input = input;\n }\n\n get type() {\n return this.#type;\n }\n\n addOutput(node: PlannerNode): void {\n this.#outputs.push(node);\n }\n\n get outputs(): PlannerNode[] {\n return this.#outputs;\n }\n\n get input(): Exclude<PlannerNode, PlannerTerminus> {\n return this.#input;\n }\n\n closestJoinOrSource(): JoinOrConnection {\n return this.#input.closestJoinOrSource();\n }\n\n propagateConstraints(\n branchPattern: number[],\n constraint: PlannerConstraint | undefined,\n from?: PlannerNode,\n planDebugger?: PlanDebugger,\n ): void {\n planDebugger?.log({\n type: 'node-constraint',\n nodeType: 'fan-out',\n node: 'FO',\n branchPattern,\n constraint,\n from: from?.kind ?? 'unknown',\n });\n\n this.#input.propagateConstraints(\n branchPattern,\n constraint,\n this,\n planDebugger,\n );\n }\n\n estimateCost(\n downstreamChildSelectivity: number,\n branchPattern: number[],\n planDebugger?: PlanDebugger,\n ): CostEstimate {\n const ret = this.#input.estimateCost(\n downstreamChildSelectivity,\n branchPattern,\n planDebugger,\n );\n\n if (planDebugger) {\n planDebugger.log({\n type: 'node-cost',\n nodeType: 'fan-out',\n node: 'FO',\n branchPattern,\n downstreamChildSelectivity,\n costEstimate: omitFanout(ret),\n });\n }\n\n return ret;\n }\n\n convertToUFO(): void {\n this.#type = 'UFO';\n }\n\n reset(): void {\n this.#type = 'FO';\n }\n\n /**\n * Propagate unlimiting when a parent join is flipped.\n * Fan-out propagates to its input.\n */\n propagateUnlimitFromFlippedJoin(): void {\n if (\n 'propagateUnlimitFromFlippedJoin' in this.#input &&\n typeof this.#input.propagateUnlimitFromFlippedJoin === 'function'\n ) {\n (\n this.#input as {propagateUnlimitFromFlippedJoin(): void}\n ).propagateUnlimitFromFlippedJoin();\n }\n }\n}\n"],"mappings":";;AAUA,IAAa,gBAAb,MAA2B;CACzB,OAAgB;CAChB;CACA,WAAmC,CAAC;CACpC;CAEA,YAAY,OAA8C;EACxD,KAAK,QAAQ;EACb,KAAK,SAAS;CAChB;CAEA,IAAI,OAAO;EACT,OAAO,KAAK;CACd;CAEA,UAAU,MAAyB;EACjC,KAAK,SAAS,KAAK,IAAI;CACzB;CAEA,IAAI,UAAyB;EAC3B,OAAO,KAAK;CACd;CAEA,IAAI,QAA+C;EACjD,OAAO,KAAK;CACd;CAEA,sBAAwC;EACtC,OAAO,KAAK,OAAO,oBAAoB;CACzC;CAEA,qBACE,eACA,YACA,MACA,cACM;EACN,cAAc,IAAI;GAChB,MAAM;GACN,UAAU;GACV,MAAM;GACN;GACA;GACA,MAAM,MAAM,QAAQ;EACtB,CAAC;EAED,KAAK,OAAO,qBACV,eACA,YACA,MACA,YACF;CACF;CAEA,aACE,4BACA,eACA,cACc;EACd,MAAM,MAAM,KAAK,OAAO,aACtB,4BACA,eACA,YACF;EAEA,IAAI,cACF,aAAa,IAAI;GACf,MAAM;GACN,UAAU;GACV,MAAM;GACN;GACA;GACA,cAAc,WAAW,GAAG;EAC9B,CAAC;EAGH,OAAO;CACT;CAEA,eAAqB;EACnB,KAAK,QAAQ;CACf;CAEA,QAAc;EACZ,KAAK,QAAQ;CACf;;;;;CAMA,kCAAwC;EACtC,IACE,qCAAqC,KAAK,UAC1C,OAAO,KAAK,OAAO,oCAAoC,YAEvD,KACO,OACL,gCAAgC;CAEtC;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-filter.js","names":[
|
|
1
|
+
{"version":3,"file":"planner-filter.js","names":[],"sources":["../../../../../zql/src/planner/planner-filter.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {NoSubqueryCondition} from '../builder/filter.ts';\nimport type {PlannerConnection} from './planner-connection.ts';\nimport type {PlannerConstraint} from './planner-constraint.ts';\nimport type {PlanDebugger} from './planner-debug.ts';\nimport {omitFanout} from './planner-node.ts';\nimport type {\n CostEstimate,\n JoinOrConnection,\n PlannerNode,\n} from './planner-node.ts';\nimport type {PlannerTerminus} from './planner-terminus.ts';\n\n/**\n * Represents a simple (non-CSQ) branch of an OR in the planner graph.\n *\n * # Why this node exists\n * Without `PlannerFilter`, the planner would not see simple branches of an\n * OR at all — `processOr` historically only admitted subquery-bearing\n * branches into the graph. That meant the cost model never got to weigh\n * the per-branch filter that the runtime actually pushes down via\n * `FetchRequest.filter` for UFI/UFO branches.\n *\n * # What it does\n * - Stores the simple branch's `NoSubqueryCondition`.\n * - Forwards `propagateConstraints` to its input (typically a `PlannerFanOut`\n * leading back to a `PlannerConnection`).\n * - When the enclosing FanIn is in `UFI` mode, registers the condition at\n * the receiving `PlannerConnection` under the current `branchPattern` via\n * `connection.setPerBranchFilter(...)`. This lets the cost model see the\n * true per-branch filter at fetch time.\n * - In `FI` mode, registration is skipped — at runtime, FI/FO uses a\n * single shared scan of the source for all branches, so per-branch filters\n * would mis-credit the source scan cost.\n *\n * # Cost\n * Pass-through. The filter node itself adds no execution cost; the only\n * effect is at the connection layer (lower estimated rows).\n *\n * # Planner/runtime branch-count mismatch\n * For OR with multiple simple branches and a flipped CSQ (e.g.\n * `or(a=1, b=2, exists(...))`), the planner builds one `PlannerFilter` per\n * simple branch, but at runtime `applyFilterWithFlips` bundles all\n * non-flipped branches into a single source fetch with `(a=1 OR b=2)` —\n * one source scan, not N. In UFI mode the planner therefore models N\n * independent source scans against the runtime's 1, slightly inflating\n * the cost of OR-with-many-simples shapes. Common `or(simple, exists)`\n * shapes have only one simple branch and are unaffected.\n */\nexport class PlannerFilter {\n readonly kind = 'filter' as const;\n readonly #input: Exclude<PlannerNode, PlannerTerminus>;\n readonly #condition: NoSubqueryCondition | undefined;\n #output?: PlannerNode | undefined;\n\n constructor(\n input: Exclude<PlannerNode, PlannerTerminus>,\n condition: NoSubqueryCondition | undefined,\n ) {\n this.#input = input;\n this.#condition = condition;\n }\n\n setOutput(node: PlannerNode): void {\n this.#output = node;\n }\n\n get output(): PlannerNode {\n assert(this.#output !== undefined, 'Output not set');\n return this.#output;\n }\n\n closestJoinOrSource(): JoinOrConnection {\n return this.#input.closestJoinOrSource();\n }\n\n propagateConstraints(\n branchPattern: number[],\n constraint: PlannerConstraint | undefined,\n from?: PlannerNode,\n planDebugger?: PlanDebugger,\n ): void {\n planDebugger?.log({\n type: 'node-constraint',\n nodeType: 'filter',\n node: 'filter',\n branchPattern,\n constraint,\n from: from?.kind ?? 'unknown',\n });\n\n // Only register the per-branch filter when we're actually going to do\n // a per-branch fetch at runtime — i.e., when our enclosing FanIn is in\n // UFI mode. In FI mode, all branches share a single source scan, so\n // applying our filter at the source would drop rows that other\n // branches need.\n if (this.#condition && from?.kind === 'fan-in' && from.type === 'UFI') {\n const conn = findParentConnection(this.#input);\n if (conn) {\n conn.setPerBranchFilter(branchPattern, this.#condition);\n }\n }\n\n this.#input.propagateConstraints(\n branchPattern,\n constraint,\n this,\n planDebugger,\n );\n }\n\n estimateCost(\n downstreamChildSelectivity: number,\n branchPattern: number[],\n planDebugger?: PlanDebugger,\n ): CostEstimate {\n const cost = this.#input.estimateCost(\n downstreamChildSelectivity,\n branchPattern,\n planDebugger,\n );\n\n if (planDebugger) {\n planDebugger.log({\n type: 'node-cost',\n nodeType: 'filter',\n node: 'filter',\n branchPattern,\n downstreamChildSelectivity,\n costEstimate: omitFanout(cost),\n });\n }\n\n return cost;\n }\n\n /**\n * Forward unlimit propagation along the parent chain (toward the source).\n */\n propagateUnlimitFromFlippedJoin(): void {\n if (\n 'propagateUnlimitFromFlippedJoin' in this.#input &&\n typeof this.#input.propagateUnlimitFromFlippedJoin === 'function'\n ) {\n (\n this.#input as {propagateUnlimitFromFlippedJoin(): void}\n ).propagateUnlimitFromFlippedJoin();\n }\n }\n\n reset(): void {\n // No mutable structural state. Per-branch filters are re-registered on\n // every propagation pass, and PlannerConnection clears its\n // #perBranchFilters in its own reset().\n }\n\n /** For debugging. */\n getConditionForDebug(): NoSubqueryCondition | undefined {\n return this.#condition;\n }\n}\n\n/**\n * Walk the input chain (toward the source) looking for the first\n * `PlannerConnection`. The simple branch's filter is meant to land on the\n * connection that the FanOut wraps — for nested AST shapes, that may be\n * several joins above.\n */\nfunction findParentConnection(\n node: Exclude<PlannerNode, PlannerTerminus>,\n): PlannerConnection | undefined {\n switch (node.kind) {\n case 'connection':\n return node;\n case 'fan-out':\n return findParentConnection(node.input);\n case 'join':\n return findParentConnection(node.parent);\n case 'fan-in':\n // Reaching a FanIn while walking up means the simple branch sits\n // above a nested OR/UFI. The connection that matters for *this*\n // simple branch's filter is the one feeding the outer FanOut, but\n // from a nested FanIn we don't have a single input to walk through.\n // Be conservative: don't register, accept that deeply nested OR\n // shapes lose this optimization.\n return undefined;\n case 'filter':\n // Shouldn't occur in graphs built by `processOr` — a PlannerFilter's\n // input is always the FanOut it's a branch of, never another filter.\n return undefined;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDA,IAAa,gBAAb,MAA2B;CACzB,OAAgB;CAChB;CACA;CACA;CAEA,YACE,OACA,WACA;EACA,KAAK,SAAS;EACd,KAAK,aAAa;CACpB;CAEA,UAAU,MAAyB;EACjC,KAAK,UAAU;CACjB;CAEA,IAAI,SAAsB;EACxB,OAAO,KAAK,YAAY,KAAA,GAAW,gBAAgB;EACnD,OAAO,KAAK;CACd;CAEA,sBAAwC;EACtC,OAAO,KAAK,OAAO,oBAAoB;CACzC;CAEA,qBACE,eACA,YACA,MACA,cACM;EACN,cAAc,IAAI;GAChB,MAAM;GACN,UAAU;GACV,MAAM;GACN;GACA;GACA,MAAM,MAAM,QAAQ;EACtB,CAAC;EAOD,IAAI,KAAK,cAAc,MAAM,SAAS,YAAY,KAAK,SAAS,OAAO;GACrE,MAAM,OAAO,qBAAqB,KAAK,MAAM;GAC7C,IAAI,MACF,KAAK,mBAAmB,eAAe,KAAK,UAAU;EAE1D;EAEA,KAAK,OAAO,qBACV,eACA,YACA,MACA,YACF;CACF;CAEA,aACE,4BACA,eACA,cACc;EACd,MAAM,OAAO,KAAK,OAAO,aACvB,4BACA,eACA,YACF;EAEA,IAAI,cACF,aAAa,IAAI;GACf,MAAM;GACN,UAAU;GACV,MAAM;GACN;GACA;GACA,cAAc,WAAW,IAAI;EAC/B,CAAC;EAGH,OAAO;CACT;;;;CAKA,kCAAwC;EACtC,IACE,qCAAqC,KAAK,UAC1C,OAAO,KAAK,OAAO,oCAAoC,YAEvD,KACO,OACL,gCAAgC;CAEtC;CAEA,QAAc,CAId;;CAGA,uBAAwD;EACtD,OAAO,KAAK;CACd;AACF;;;;;;;AAQA,SAAS,qBACP,MAC+B;CAC/B,QAAQ,KAAK,MAAb;EACE,KAAK,cACH,OAAO;EACT,KAAK,WACH,OAAO,qBAAqB,KAAK,KAAK;EACxC,KAAK,QACH,OAAO,qBAAqB,KAAK,MAAM;EACzC,KAAK,UAOH;EACF,KAAK,UAGH;CACJ;AACF"}
|
|
@@ -302,11 +302,7 @@ function findFIAndJoins(fo) {
|
|
|
302
302
|
case "fan-out":
|
|
303
303
|
queue.push(...node.outputs);
|
|
304
304
|
break;
|
|
305
|
-
case "fan-in":
|
|
306
|
-
fi = node;
|
|
307
|
-
break;
|
|
308
|
-
case "connection": break;
|
|
309
|
-
case "terminus": break;
|
|
305
|
+
case "fan-in": fi = node;
|
|
310
306
|
}
|
|
311
307
|
}
|
|
312
308
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-graph.js","names":["#sources","#terminus","#validateSnapshotShape","#restoreConnections","#restoreJoins","#restoreFanNodes"],"sources":["../../../../../zql/src/planner/planner-graph.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {PlannerConnection} from './planner-connection.ts';\nimport type {PlannerConstraint} from './planner-constraint.ts';\nimport type {PlanDebugger} from './planner-debug.ts';\nimport type {PlannerFanIn} from './planner-fan-in.ts';\nimport type {PlannerFanOut} from './planner-fan-out.ts';\nimport type {PlannerFilter} from './planner-filter.ts';\nimport type {PlannerJoin} from './planner-join.ts';\nimport {omitFanout} from './planner-node.ts';\nimport type {CostEstimate, PlannerNode} from './planner-node.ts';\nimport {PlannerSource, type ConnectionCostModel} from './planner-source.ts';\nimport type {PlannerTerminus} from './planner-terminus.ts';\n\n/**\n * Captured state of a plan for comparison and restoration.\n */\nexport type PlanState = {\n connections: Array<{limit: number | undefined}>;\n joins: Array<{type: 'semi' | 'flipped'}>;\n fanOuts: Array<{type: 'FO' | 'UFO'}>;\n fanIns: Array<{type: 'FI' | 'UFI'}>;\n connectionConstraints: Array<Map<string, PlannerConstraint | undefined>>;\n};\n\n/**\n * Maximum number of flippable joins to attempt exhaustive enumeration.\n * With n flippable joins, we explore 2^n plans.\n * 10 joins = 1024 plans (~100-200ms), 12 joins = 4096 plans (~400ms - 1 second)\n */\nconst MAX_FLIPPABLE_JOINS = 9;\n\n/**\n * Cached information about FanOut→FanIn relationships.\n * Computed once during planning to avoid redundant BFS traversals.\n */\ntype FOFIInfo = {\n fi: PlannerFanIn | undefined;\n joinsBetween: PlannerJoin[];\n};\n\nexport class PlannerGraph {\n // Sources indexed by table name\n readonly #sources = new Map<string, PlannerSource>();\n\n // The final output node where constraint propagation starts\n #terminus: PlannerTerminus | undefined = undefined;\n\n // Collections of nodes with mutable planning state\n joins: PlannerJoin[] = [];\n fanOuts: PlannerFanOut[] = [];\n fanIns: PlannerFanIn[] = [];\n connections: PlannerConnection[] = [];\n filters: PlannerFilter[] = [];\n\n /**\n * Reset all planning state back to initial values for another planning pass.\n * Resets only mutable planning state - graph structure is unchanged.\n *\n * This allows replanning the same query graph with different strategies.\n */\n resetPlanningState() {\n for (const j of this.joins) j.reset();\n for (const fo of this.fanOuts) fo.reset();\n for (const fi of this.fanIns) fi.reset();\n for (const c of this.connections) c.reset();\n for (const f of this.filters) f.reset();\n }\n\n /**\n * Create and register a source (table) in the graph.\n */\n addSource(name: string, model: ConnectionCostModel): PlannerSource {\n assert(\n !this.#sources.has(name),\n `Source ${name} already exists in the graph`,\n );\n const source = new PlannerSource(name, model);\n this.#sources.set(name, source);\n return source;\n }\n\n /**\n * Get a source by table name.\n */\n getSource(name: string): PlannerSource {\n const source = this.#sources.get(name);\n assert(source !== undefined, `Source ${name} not found in the graph`);\n return source;\n }\n\n /**\n * Check if a source exists by table name.\n */\n hasSource(name: string): boolean {\n return this.#sources.has(name);\n }\n\n /**\n * Set the terminus (final output) node of the graph.\n * Constraint propagation starts from this node.\n */\n setTerminus(terminus: PlannerTerminus): void {\n this.#terminus = terminus;\n }\n\n /**\n * Initiate constraint propagation from the terminus node.\n * This sends constraints up through the graph to update\n * connection cost estimates.\n */\n propagateConstraints(planDebugger?: PlanDebugger): void {\n assert(\n this.#terminus !== undefined,\n 'Cannot propagate constraints without a terminus node',\n );\n this.#terminus.propagateConstraints(planDebugger);\n }\n\n /**\n * Calculate total cost of the current plan.\n * Total cost includes both startup cost (one-time, e.g., sorting) and running cost.\n */\n getTotalCost(planDebugger?: PlanDebugger): number {\n const estimate = must(this.#terminus).estimateCost(planDebugger);\n return estimate.cost + estimate.startupCost;\n }\n\n /**\n * Estimates the cost of the current plan, e.g. the one {@link plan} chose,\n * leaving each connection's {@link PlannerConnection.accesses} for it.\n *\n * This derives the same state that {@link plan} derives for each plan it\n * tries, so it also works for a graph {@link plan} had nothing to choose\n * for. For a plan that {@link plan} restored it changes nothing.\n */\n estimateCurrentPlan(): CostEstimate {\n checkAndConvertFOFI(buildFOFICache(this));\n propagateUnlimitForFlippedJoins(this);\n this.propagateConstraints();\n return must(this.#terminus).estimateCost();\n }\n\n /**\n * Capture a lightweight snapshot of the current planning state.\n * Used for backtracking during multi-start greedy search.\n *\n * Captures mutable state including pinned flags, join types, and\n * constraint maps to avoid needing repropagation on restore.\n *\n * @returns A snapshot that can be restored via restorePlanningSnapshot()\n */\n capturePlanningSnapshot(): PlanState {\n return {\n connections: this.connections.map(c => ({\n limit: c.limit,\n })),\n joins: this.joins.map(j => ({type: j.type})),\n fanOuts: this.fanOuts.map(fo => ({type: fo.type})),\n fanIns: this.fanIns.map(fi => ({type: fi.type})),\n connectionConstraints: this.connections.map(c => c.captureConstraints()),\n };\n }\n\n /**\n * Restore planning state from a previously captured snapshot.\n * Used for backtracking when a planning attempt fails.\n *\n * Restores pinned flags, join types, and constraint maps, eliminating\n * the need for repropagation.\n *\n * @param state - Snapshot created by capturePlanningSnapshot()\n */\n restorePlanningSnapshot(state: PlanState): void {\n this.#validateSnapshotShape(state);\n this.#restoreConnections(state);\n this.#restoreJoins(state);\n this.#restoreFanNodes(state);\n }\n\n /**\n * Validate that snapshot shape matches current graph structure.\n */\n #validateSnapshotShape(state: PlanState): void {\n assert(\n this.connections.length === state.connections.length,\n 'Plan state mismatch: connections',\n );\n assert(\n this.joins.length === state.joins.length,\n 'Plan state mismatch: joins',\n );\n assert(\n this.fanOuts.length === state.fanOuts.length,\n 'Plan state mismatch: fanOuts',\n );\n assert(\n this.fanIns.length === state.fanIns.length,\n 'Plan state mismatch: fanIns',\n );\n assert(\n this.connections.length === state.connectionConstraints.length,\n 'Plan state mismatch: connectionConstraints',\n );\n }\n\n /**\n * Restore connection pinned flags, limits, and constraint maps.\n */\n #restoreConnections(state: PlanState): void {\n for (let i = 0; i < this.connections.length; i++) {\n this.connections[i].limit = state.connections[i].limit;\n this.connections[i].restoreConstraints(state.connectionConstraints[i]);\n }\n }\n\n /**\n * Restore join types and pinned flags.\n */\n #restoreJoins(state: PlanState): void {\n for (let i = 0; i < this.joins.length; i++) {\n const join = this.joins[i];\n const targetState = state.joins[i];\n\n // Reset to initial state first\n join.reset();\n\n // Apply target state\n if (targetState.type === 'flipped' && join.type !== 'flipped') {\n join.flip();\n }\n assert(\n targetState.type === join.type,\n 'join is not in the correct state after reset',\n );\n }\n }\n\n /**\n * Restore FanOut and FanIn types.\n */\n #restoreFanNodes(state: PlanState): void {\n for (let i = 0; i < this.fanOuts.length; i++) {\n const fo = this.fanOuts[i];\n const targetType = state.fanOuts[i].type;\n if (targetType === 'UFO' && fo.type === 'FO') {\n fo.convertToUFO();\n }\n }\n\n for (let i = 0; i < this.fanIns.length; i++) {\n const fi = this.fanIns[i];\n const targetType = state.fanIns[i].type;\n if (targetType === 'UFI' && fi.type === 'FI') {\n fi.convertToUFI();\n }\n }\n }\n\n /**\n * Main planning algorithm using exhaustive join flip enumeration.\n *\n * Enumerates all possible flip patterns for flippable joins (2^n for n flippable joins).\n * Each pattern represents a different query execution plan. We evaluate the cost of each\n * plan and select the one with the lowest cost.\n *\n * Connections are used only for cost estimation - the flip patterns determine the plan.\n * FanOut/FanIn states (FO/UFO and FI/UFI) are automatically derived from join flip states.\n *\n * @param planDebugger - Optional debugger to receive structured events during planning\n * @param lc - Optional logger for warnings\n */\n plan(planDebugger?: PlanDebugger, lc?: LogContext): void {\n // Get all flippable joins\n const flippableJoins = this.joins.filter(j => j.isFlippable());\n\n // Too many flippable joins - skip optimization and run as-is\n if (flippableJoins.length > MAX_FLIPPABLE_JOINS) {\n lc?.warn?.(\n `Query has ${flippableJoins.length} EXISTS checks which would require ` +\n `${2 ** flippableJoins.length} plan evaluations. Skipping optimization.`,\n );\n return;\n }\n\n // Build FO→FI cache once to avoid redundant BFS traversals in each iteration\n const fofiCache = buildFOFICache(this);\n\n const numPatterns =\n flippableJoins.length === 0 ? 0 : 2 ** flippableJoins.length;\n let bestCost = Infinity;\n let bestPlan: PlanState | undefined = undefined;\n let bestAttemptNumber = -1;\n\n // Enumerate all flip patterns\n for (let pattern = 0; pattern < numPatterns; pattern++) {\n // Reset to initial state\n this.resetPlanningState();\n\n if (planDebugger) {\n planDebugger.log({\n type: 'attempt-start',\n attemptNumber: pattern,\n totalAttempts: numPatterns,\n });\n }\n\n // Apply flip pattern (treat pattern as bitmask)\n // Bit i set to 1 means flip join i\n for (let i = 0; i < flippableJoins.length; i++) {\n if (pattern & (1 << i)) {\n flippableJoins[i].flip();\n }\n }\n\n // Derive FO/UFO and FI/UFI states from join flip states\n checkAndConvertFOFI(fofiCache);\n\n // Propagate unlimiting for flipped joins\n propagateUnlimitForFlippedJoins(this);\n\n // Propagate constraints through the graph\n this.propagateConstraints(planDebugger);\n\n if (planDebugger) {\n planDebugger.log({\n type: 'constraints-propagated',\n attemptNumber: pattern,\n connectionConstraints: this.connections.map(c => {\n const constraintCosts = c.getConstraintCostsForDebug();\n const constraintCostsWithoutFanout: Record<\n string,\n Omit<(typeof constraintCosts)[string], 'fanout'>\n > = {};\n for (const [key, cost] of Object.entries(constraintCosts)) {\n constraintCostsWithoutFanout[key] = omitFanout(cost);\n }\n return {\n connection: c.name,\n constraints: c.getConstraintsForDebug(),\n constraintCosts: constraintCostsWithoutFanout,\n };\n }),\n });\n }\n\n // Evaluate this plan\n const totalCost = this.getTotalCost(planDebugger);\n\n if (planDebugger) {\n planDebugger.log({\n type: 'plan-complete',\n attemptNumber: pattern,\n totalCost,\n flipPattern: pattern, // Bitmask of which joins are flipped\n planSnapshot: this.capturePlanningSnapshot(),\n joinStates: this.joins.map(j => {\n const info = j.getDebugInfo();\n return {\n join: info.name,\n type: info.type,\n };\n }),\n });\n }\n\n // Track best plan\n if (totalCost < bestCost) {\n bestCost = totalCost;\n bestPlan = this.capturePlanningSnapshot();\n bestAttemptNumber = pattern;\n }\n }\n\n // Restore best plan\n if (bestPlan) {\n this.restorePlanningSnapshot(bestPlan);\n // Propagate constraints to ensure all derived state is consistent\n this.propagateConstraints(planDebugger);\n\n if (planDebugger) {\n planDebugger.log({\n type: 'best-plan-selected',\n bestAttemptNumber,\n totalCost: bestCost,\n flipPattern: bestAttemptNumber, // The best attempt number is also the flip pattern\n joinStates: this.joins.map(j => ({\n join: j.getName(),\n type: j.type,\n })),\n });\n }\n } else {\n assert(\n numPatterns === 0,\n 'no plan was found but flippable joins did exist!',\n );\n }\n }\n}\n\n/**\n * Build cache of FO→FI relationships and joins between them.\n * Called once at the start of planning to avoid redundant BFS traversals.\n */\nfunction buildFOFICache(graph: PlannerGraph): Map<PlannerFanOut, FOFIInfo> {\n const cache = new Map<PlannerFanOut, FOFIInfo>();\n\n for (const fo of graph.fanOuts) {\n const info = findFIAndJoins(fo);\n cache.set(fo, info);\n }\n\n return cache;\n}\n\n/**\n * Check if any joins downstream of a FanOut (before reaching FanIn) are flipped.\n * If so, convert the FO to UFO and the FI to UFI.\n *\n * This must be called after join flipping and before propagateConstraints.\n */\nfunction checkAndConvertFOFI(fofiCache: Map<PlannerFanOut, FOFIInfo>): void {\n for (const [fo, info] of fofiCache) {\n const hasFlippedJoin = info.joinsBetween.some(j => j.type === 'flipped');\n if (info.fi && hasFlippedJoin) {\n fo.convertToUFO();\n info.fi.convertToUFI();\n }\n }\n}\n\n/**\n * Traverse from a FanOut through its outputs to find the corresponding FanIn\n * and collect all joins along the way.\n */\nfunction findFIAndJoins(fo: PlannerFanOut): FOFIInfo {\n const joinsBetween: PlannerJoin[] = [];\n let fi: PlannerFanIn | undefined = undefined;\n\n // BFS through FO outputs to find FI and collect joins\n const queue: PlannerNode[] = [...fo.outputs];\n const visited = new Set<PlannerNode>();\n\n while (queue.length > 0) {\n const node = must(queue.shift());\n if (visited.has(node)) continue;\n visited.add(node);\n\n switch (node.kind) {\n case 'join':\n joinsBetween.push(node);\n queue.push(node.output);\n break;\n case 'fan-out':\n // Nested FO - traverse its outputs\n queue.push(...node.outputs);\n break;\n case 'fan-in':\n // Found the FI - this is the boundary, don't traverse further\n fi = node;\n break;\n case 'connection':\n // Shouldn't happen in a well-formed graph\n break;\n case 'terminus':\n // Reached the end without finding FI\n break;\n }\n }\n\n return {fi, joinsBetween};\n}\n\n/**\n * Propagate unlimiting to all flipped joins in the graph.\n * When a join is flipped, its child becomes the outer loop and should no longer\n * be limited by EXISTS semantics.\n *\n * This must be called after join flipping and before propagateConstraints.\n */\nfunction propagateUnlimitForFlippedJoins(graph: PlannerGraph): void {\n for (const join of graph.joins) {\n if (join.type === 'flipped') {\n join.propagateUnlimit();\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"planner-graph.js","names":[],"sources":["../../../../../zql/src/planner/planner-graph.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {PlannerConnection} from './planner-connection.ts';\nimport type {PlannerConstraint} from './planner-constraint.ts';\nimport type {PlanDebugger} from './planner-debug.ts';\nimport type {PlannerFanIn} from './planner-fan-in.ts';\nimport type {PlannerFanOut} from './planner-fan-out.ts';\nimport type {PlannerFilter} from './planner-filter.ts';\nimport type {PlannerJoin} from './planner-join.ts';\nimport {omitFanout} from './planner-node.ts';\nimport type {CostEstimate, PlannerNode} from './planner-node.ts';\nimport {PlannerSource, type ConnectionCostModel} from './planner-source.ts';\nimport type {PlannerTerminus} from './planner-terminus.ts';\n\n/**\n * Captured state of a plan for comparison and restoration.\n */\nexport type PlanState = {\n connections: Array<{limit: number | undefined}>;\n joins: Array<{type: 'semi' | 'flipped'}>;\n fanOuts: Array<{type: 'FO' | 'UFO'}>;\n fanIns: Array<{type: 'FI' | 'UFI'}>;\n connectionConstraints: Array<Map<string, PlannerConstraint | undefined>>;\n};\n\n/**\n * Maximum number of flippable joins to attempt exhaustive enumeration.\n * With n flippable joins, we explore 2^n plans.\n * 10 joins = 1024 plans (~100-200ms), 12 joins = 4096 plans (~400ms - 1 second)\n */\nconst MAX_FLIPPABLE_JOINS = 9;\n\n/**\n * Cached information about FanOut→FanIn relationships.\n * Computed once during planning to avoid redundant BFS traversals.\n */\ntype FOFIInfo = {\n fi: PlannerFanIn | undefined;\n joinsBetween: PlannerJoin[];\n};\n\nexport class PlannerGraph {\n // Sources indexed by table name\n readonly #sources = new Map<string, PlannerSource>();\n\n // The final output node where constraint propagation starts\n #terminus: PlannerTerminus | undefined = undefined;\n\n // Collections of nodes with mutable planning state\n joins: PlannerJoin[] = [];\n fanOuts: PlannerFanOut[] = [];\n fanIns: PlannerFanIn[] = [];\n connections: PlannerConnection[] = [];\n filters: PlannerFilter[] = [];\n\n /**\n * Reset all planning state back to initial values for another planning pass.\n * Resets only mutable planning state - graph structure is unchanged.\n *\n * This allows replanning the same query graph with different strategies.\n */\n resetPlanningState() {\n for (const j of this.joins) j.reset();\n for (const fo of this.fanOuts) fo.reset();\n for (const fi of this.fanIns) fi.reset();\n for (const c of this.connections) c.reset();\n for (const f of this.filters) f.reset();\n }\n\n /**\n * Create and register a source (table) in the graph.\n */\n addSource(name: string, model: ConnectionCostModel): PlannerSource {\n assert(\n !this.#sources.has(name),\n `Source ${name} already exists in the graph`,\n );\n const source = new PlannerSource(name, model);\n this.#sources.set(name, source);\n return source;\n }\n\n /**\n * Get a source by table name.\n */\n getSource(name: string): PlannerSource {\n const source = this.#sources.get(name);\n assert(source !== undefined, `Source ${name} not found in the graph`);\n return source;\n }\n\n /**\n * Check if a source exists by table name.\n */\n hasSource(name: string): boolean {\n return this.#sources.has(name);\n }\n\n /**\n * Set the terminus (final output) node of the graph.\n * Constraint propagation starts from this node.\n */\n setTerminus(terminus: PlannerTerminus): void {\n this.#terminus = terminus;\n }\n\n /**\n * Initiate constraint propagation from the terminus node.\n * This sends constraints up through the graph to update\n * connection cost estimates.\n */\n propagateConstraints(planDebugger?: PlanDebugger): void {\n assert(\n this.#terminus !== undefined,\n 'Cannot propagate constraints without a terminus node',\n );\n this.#terminus.propagateConstraints(planDebugger);\n }\n\n /**\n * Calculate total cost of the current plan.\n * Total cost includes both startup cost (one-time, e.g., sorting) and running cost.\n */\n getTotalCost(planDebugger?: PlanDebugger): number {\n const estimate = must(this.#terminus).estimateCost(planDebugger);\n return estimate.cost + estimate.startupCost;\n }\n\n /**\n * Estimates the cost of the current plan, e.g. the one {@link plan} chose,\n * leaving each connection's {@link PlannerConnection.accesses} for it.\n *\n * This derives the same state that {@link plan} derives for each plan it\n * tries, so it also works for a graph {@link plan} had nothing to choose\n * for. For a plan that {@link plan} restored it changes nothing.\n */\n estimateCurrentPlan(): CostEstimate {\n checkAndConvertFOFI(buildFOFICache(this));\n propagateUnlimitForFlippedJoins(this);\n this.propagateConstraints();\n return must(this.#terminus).estimateCost();\n }\n\n /**\n * Capture a lightweight snapshot of the current planning state.\n * Used for backtracking during multi-start greedy search.\n *\n * Captures mutable state including pinned flags, join types, and\n * constraint maps to avoid needing repropagation on restore.\n *\n * @returns A snapshot that can be restored via restorePlanningSnapshot()\n */\n capturePlanningSnapshot(): PlanState {\n return {\n connections: this.connections.map(c => ({\n limit: c.limit,\n })),\n joins: this.joins.map(j => ({type: j.type})),\n fanOuts: this.fanOuts.map(fo => ({type: fo.type})),\n fanIns: this.fanIns.map(fi => ({type: fi.type})),\n connectionConstraints: this.connections.map(c => c.captureConstraints()),\n };\n }\n\n /**\n * Restore planning state from a previously captured snapshot.\n * Used for backtracking when a planning attempt fails.\n *\n * Restores pinned flags, join types, and constraint maps, eliminating\n * the need for repropagation.\n *\n * @param state - Snapshot created by capturePlanningSnapshot()\n */\n restorePlanningSnapshot(state: PlanState): void {\n this.#validateSnapshotShape(state);\n this.#restoreConnections(state);\n this.#restoreJoins(state);\n this.#restoreFanNodes(state);\n }\n\n /**\n * Validate that snapshot shape matches current graph structure.\n */\n #validateSnapshotShape(state: PlanState): void {\n assert(\n this.connections.length === state.connections.length,\n 'Plan state mismatch: connections',\n );\n assert(\n this.joins.length === state.joins.length,\n 'Plan state mismatch: joins',\n );\n assert(\n this.fanOuts.length === state.fanOuts.length,\n 'Plan state mismatch: fanOuts',\n );\n assert(\n this.fanIns.length === state.fanIns.length,\n 'Plan state mismatch: fanIns',\n );\n assert(\n this.connections.length === state.connectionConstraints.length,\n 'Plan state mismatch: connectionConstraints',\n );\n }\n\n /**\n * Restore connection pinned flags, limits, and constraint maps.\n */\n #restoreConnections(state: PlanState): void {\n for (let i = 0; i < this.connections.length; i++) {\n this.connections[i].limit = state.connections[i].limit;\n this.connections[i].restoreConstraints(state.connectionConstraints[i]);\n }\n }\n\n /**\n * Restore join types and pinned flags.\n */\n #restoreJoins(state: PlanState): void {\n for (let i = 0; i < this.joins.length; i++) {\n const join = this.joins[i];\n const targetState = state.joins[i];\n\n // Reset to initial state first\n join.reset();\n\n // Apply target state\n if (targetState.type === 'flipped' && join.type !== 'flipped') {\n join.flip();\n }\n assert(\n targetState.type === join.type,\n 'join is not in the correct state after reset',\n );\n }\n }\n\n /**\n * Restore FanOut and FanIn types.\n */\n #restoreFanNodes(state: PlanState): void {\n for (let i = 0; i < this.fanOuts.length; i++) {\n const fo = this.fanOuts[i];\n const targetType = state.fanOuts[i].type;\n if (targetType === 'UFO' && fo.type === 'FO') {\n fo.convertToUFO();\n }\n }\n\n for (let i = 0; i < this.fanIns.length; i++) {\n const fi = this.fanIns[i];\n const targetType = state.fanIns[i].type;\n if (targetType === 'UFI' && fi.type === 'FI') {\n fi.convertToUFI();\n }\n }\n }\n\n /**\n * Main planning algorithm using exhaustive join flip enumeration.\n *\n * Enumerates all possible flip patterns for flippable joins (2^n for n flippable joins).\n * Each pattern represents a different query execution plan. We evaluate the cost of each\n * plan and select the one with the lowest cost.\n *\n * Connections are used only for cost estimation - the flip patterns determine the plan.\n * FanOut/FanIn states (FO/UFO and FI/UFI) are automatically derived from join flip states.\n *\n * @param planDebugger - Optional debugger to receive structured events during planning\n * @param lc - Optional logger for warnings\n */\n plan(planDebugger?: PlanDebugger, lc?: LogContext): void {\n // Get all flippable joins\n const flippableJoins = this.joins.filter(j => j.isFlippable());\n\n // Too many flippable joins - skip optimization and run as-is\n if (flippableJoins.length > MAX_FLIPPABLE_JOINS) {\n lc?.warn?.(\n `Query has ${flippableJoins.length} EXISTS checks which would require ` +\n `${2 ** flippableJoins.length} plan evaluations. Skipping optimization.`,\n );\n return;\n }\n\n // Build FO→FI cache once to avoid redundant BFS traversals in each iteration\n const fofiCache = buildFOFICache(this);\n\n const numPatterns =\n flippableJoins.length === 0 ? 0 : 2 ** flippableJoins.length;\n let bestCost = Infinity;\n let bestPlan: PlanState | undefined = undefined;\n let bestAttemptNumber = -1;\n\n // Enumerate all flip patterns\n for (let pattern = 0; pattern < numPatterns; pattern++) {\n // Reset to initial state\n this.resetPlanningState();\n\n if (planDebugger) {\n planDebugger.log({\n type: 'attempt-start',\n attemptNumber: pattern,\n totalAttempts: numPatterns,\n });\n }\n\n // Apply flip pattern (treat pattern as bitmask)\n // Bit i set to 1 means flip join i\n for (let i = 0; i < flippableJoins.length; i++) {\n if (pattern & (1 << i)) {\n flippableJoins[i].flip();\n }\n }\n\n // Derive FO/UFO and FI/UFI states from join flip states\n checkAndConvertFOFI(fofiCache);\n\n // Propagate unlimiting for flipped joins\n propagateUnlimitForFlippedJoins(this);\n\n // Propagate constraints through the graph\n this.propagateConstraints(planDebugger);\n\n if (planDebugger) {\n planDebugger.log({\n type: 'constraints-propagated',\n attemptNumber: pattern,\n connectionConstraints: this.connections.map(c => {\n const constraintCosts = c.getConstraintCostsForDebug();\n const constraintCostsWithoutFanout: Record<\n string,\n Omit<(typeof constraintCosts)[string], 'fanout'>\n > = {};\n for (const [key, cost] of Object.entries(constraintCosts)) {\n constraintCostsWithoutFanout[key] = omitFanout(cost);\n }\n return {\n connection: c.name,\n constraints: c.getConstraintsForDebug(),\n constraintCosts: constraintCostsWithoutFanout,\n };\n }),\n });\n }\n\n // Evaluate this plan\n const totalCost = this.getTotalCost(planDebugger);\n\n if (planDebugger) {\n planDebugger.log({\n type: 'plan-complete',\n attemptNumber: pattern,\n totalCost,\n flipPattern: pattern, // Bitmask of which joins are flipped\n planSnapshot: this.capturePlanningSnapshot(),\n joinStates: this.joins.map(j => {\n const info = j.getDebugInfo();\n return {\n join: info.name,\n type: info.type,\n };\n }),\n });\n }\n\n // Track best plan\n if (totalCost < bestCost) {\n bestCost = totalCost;\n bestPlan = this.capturePlanningSnapshot();\n bestAttemptNumber = pattern;\n }\n }\n\n // Restore best plan\n if (bestPlan) {\n this.restorePlanningSnapshot(bestPlan);\n // Propagate constraints to ensure all derived state is consistent\n this.propagateConstraints(planDebugger);\n\n if (planDebugger) {\n planDebugger.log({\n type: 'best-plan-selected',\n bestAttemptNumber,\n totalCost: bestCost,\n flipPattern: bestAttemptNumber, // The best attempt number is also the flip pattern\n joinStates: this.joins.map(j => ({\n join: j.getName(),\n type: j.type,\n })),\n });\n }\n } else {\n assert(\n numPatterns === 0,\n 'no plan was found but flippable joins did exist!',\n );\n }\n }\n}\n\n/**\n * Build cache of FO→FI relationships and joins between them.\n * Called once at the start of planning to avoid redundant BFS traversals.\n */\nfunction buildFOFICache(graph: PlannerGraph): Map<PlannerFanOut, FOFIInfo> {\n const cache = new Map<PlannerFanOut, FOFIInfo>();\n\n for (const fo of graph.fanOuts) {\n const info = findFIAndJoins(fo);\n cache.set(fo, info);\n }\n\n return cache;\n}\n\n/**\n * Check if any joins downstream of a FanOut (before reaching FanIn) are flipped.\n * If so, convert the FO to UFO and the FI to UFI.\n *\n * This must be called after join flipping and before propagateConstraints.\n */\nfunction checkAndConvertFOFI(fofiCache: Map<PlannerFanOut, FOFIInfo>): void {\n for (const [fo, info] of fofiCache) {\n const hasFlippedJoin = info.joinsBetween.some(j => j.type === 'flipped');\n if (info.fi && hasFlippedJoin) {\n fo.convertToUFO();\n info.fi.convertToUFI();\n }\n }\n}\n\n/**\n * Traverse from a FanOut through its outputs to find the corresponding FanIn\n * and collect all joins along the way.\n */\nfunction findFIAndJoins(fo: PlannerFanOut): FOFIInfo {\n const joinsBetween: PlannerJoin[] = [];\n let fi: PlannerFanIn | undefined = undefined;\n\n // BFS through FO outputs to find FI and collect joins\n const queue: PlannerNode[] = [...fo.outputs];\n const visited = new Set<PlannerNode>();\n\n while (queue.length > 0) {\n const node = must(queue.shift());\n if (visited.has(node)) continue;\n visited.add(node);\n\n switch (node.kind) {\n case 'join':\n joinsBetween.push(node);\n queue.push(node.output);\n break;\n case 'fan-out':\n // Nested FO - traverse its outputs\n queue.push(...node.outputs);\n break;\n case 'fan-in':\n // Found the FI - this is the boundary, don't traverse further\n fi = node;\n break;\n case 'connection':\n // Shouldn't happen in a well-formed graph\n break;\n case 'terminus':\n // Reached the end without finding FI\n break;\n }\n }\n\n return {fi, joinsBetween};\n}\n\n/**\n * Propagate unlimiting to all flipped joins in the graph.\n * When a join is flipped, its child becomes the outer loop and should no longer\n * be limited by EXISTS semantics.\n *\n * This must be called after join flipping and before propagateConstraints.\n */\nfunction propagateUnlimitForFlippedJoins(graph: PlannerGraph): void {\n for (const join of graph.joins) {\n if (join.type === 'flipped') {\n join.propagateUnlimit();\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-join.js","names":["#parent","#child","#parentConstraint","#childConstraint","#flippable","#initialType","#type","#output"],"sources":["../../../../../zql/src/planner/planner-join.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {getMultiConstraintChunkSize} from '../ivm/flipped-join.ts';\nimport {\n mergeConstraints,\n type PlannerConstraint,\n} from './planner-constraint.ts';\nimport type {PlanDebugger} from './planner-debug.ts';\nimport {omitFanout} from './planner-node.ts';\nimport type {\n CostEstimate,\n JoinOrConnection,\n PlannerNode,\n} from './planner-node.ts';\nimport type {PlannerTerminus} from './planner-terminus.ts';\n\n/**\n * Translate constraints for a flipped join from parent space to child space.\n * Matches the runtime behavior of FlippedJoin.fetch() which translates\n * parent constraints to child constraints using index-based key mapping.\n *\n * Example:\n * parentConstraint = {issueID: undefined, projectID: undefined}\n * childConstraint = {id: undefined, projectID: undefined}\n * incomingConstraint = {issueID: 5}\n * result = {id: 5} // issueID at index 0 maps to id at index 0\n */\nfunction translateConstraintsForFlippedJoin(\n incomingConstraint: PlannerConstraint | undefined,\n parentConstraint: PlannerConstraint,\n childConstraint: PlannerConstraint,\n): PlannerConstraint | undefined {\n if (!incomingConstraint) return undefined;\n\n const parentKeys = Object.keys(parentConstraint);\n const childKeys = Object.keys(childConstraint);\n const translated: PlannerConstraint = {};\n\n for (const [key, value] of Object.entries(incomingConstraint)) {\n const index = parentKeys.indexOf(key);\n if (index !== -1) {\n // Found this key in parent at position `index`\n // Map to child key at same position\n translated[childKeys[index]] = value;\n }\n }\n\n return Object.keys(translated).length > 0 ? translated : undefined;\n}\n\n/**\n * Semi-join overhead multiplier.\n *\n * Semi-joins represent correlated subqueries (EXISTS checks) which have\n * execution overhead compared to flipped joins, even when logical row counts\n * are identical. This overhead comes from:\n * - Need to execute a separate correlation check for each parent row\n * - Cannot leverage combined constraint checking as effectively as flipped joins\n *\n * A multiplier of 1.5 means semi-joins are estimated to be ~50% more expensive\n * than equivalent flipped joins, which empirically matches observed performance\n * differences in production workloads (e.g., 1.7x in zbugs benchmarks).\n *\n * Flipped joins have a different overhead in that they become unlimited. This\n * is accounted for when propagating unlimits rather than here.\n */\n// const SEMI_JOIN_OVERHEAD_MULTIPLIER = 1.5;\n\n/**\n * Represents a join between two data streams (parent and child).\n *\n * # Dual-State Pattern\n * Like all planner nodes, PlannerJoin separates:\n * 1. IMMUTABLE STRUCTURE: Parent/child nodes, constraints, flippability\n * 2. MUTABLE STATE: Join type (semi/flipped), pinned status\n *\n * # Join Flipping\n * A join can be in two states:\n * - 'semi': Parent is outer loop, child is inner (semi-join for EXISTS)\n * - 'flipped': Child is outer loop, parent is inner\n *\n * Flipping is the key optimization: choosing which table scans first.\n * NOT EXISTS joins cannot be flipped (#flippable = false).\n *\n * # Constraint Propagation\n * - Semi-join: Sends childConstraint to child, forwards received constraints to parent\n * - Flipped join: Sends undefined to child, merges parentConstraint with received to parent\n * - Unpinned join: Only forwards constraints to parent (doesn't constrain child yet)\n *\n * # Lifecycle\n * 1. Construct with immutable structure (parent, child, constraints, flippability)\n * 2. Wire to output node during graph construction\n * 3. Planning calls flipIfNeeded() based on connection selection order\n * 4. pin() locks the join type once chosen\n * 5. reset() clears mutable state (type → 'semi', pinned → false)\n */\nexport class PlannerJoin {\n readonly kind = 'join' as const;\n\n readonly #parent: Exclude<PlannerNode, PlannerTerminus>;\n readonly #child: Exclude<PlannerNode, PlannerTerminus>;\n readonly #parentConstraint: PlannerConstraint;\n readonly #childConstraint: PlannerConstraint;\n readonly #flippable: boolean;\n readonly planId: number;\n #output?: PlannerNode | undefined; // Set once during graph construction\n\n // Reset between planning attempts\n #type: 'semi' | 'flipped';\n readonly #initialType: 'semi' | 'flipped';\n\n constructor(\n parent: Exclude<PlannerNode, PlannerTerminus>,\n child: Exclude<PlannerNode, PlannerTerminus>,\n parentConstraint: PlannerConstraint,\n childConstraint: PlannerConstraint,\n flippable: boolean,\n planId: number,\n initialType: 'semi' | 'flipped' = 'semi',\n ) {\n this.#type = initialType;\n this.#initialType = initialType;\n this.#parent = parent;\n this.#child = child;\n this.#childConstraint = childConstraint;\n this.#parentConstraint = parentConstraint;\n this.#flippable = flippable;\n this.planId = planId;\n }\n\n setOutput(node: PlannerNode): void {\n this.#output = node;\n }\n\n get output(): PlannerNode {\n assert(this.#output !== undefined, 'Output not set');\n return this.#output;\n }\n\n get parent(): Exclude<PlannerNode, PlannerTerminus> {\n return this.#parent;\n }\n\n get child(): Exclude<PlannerNode, PlannerTerminus> {\n return this.#child;\n }\n\n closestJoinOrSource(): JoinOrConnection {\n return 'join';\n }\n\n flipIfNeeded(input: PlannerNode): void {\n if (input === this.#child) {\n this.flip();\n } else {\n assert(\n input === this.#parent,\n 'Can only flip a join from one of its inputs',\n );\n }\n }\n\n flip(): void {\n assert(this.#type === 'semi', 'Can only flip a semi-join');\n if (!this.#flippable) {\n throw new UnflippableJoinError(\n 'Cannot flip a non-flippable join (e.g., NOT EXISTS)',\n );\n }\n this.#type = 'flipped';\n }\n\n get type(): 'semi' | 'flipped' {\n return this.#type;\n }\n isFlippable(): boolean {\n return this.#flippable;\n }\n\n /**\n * Propagate unlimiting when this join is flipped.\n * When a join is flipped:\n * 1. Child becomes outer loop → produces all rows (unlimited)\n * 2. Parent is fetched once per child row → effectively unlimited\n *\n * Example: If child produces 896 rows, parent is fetched 896 times.\n * Even if each fetch returns 1 row, parent produces 896 total rows.\n *\n * Propagation rules:\n * - Connection: call unlimit()\n * - Semi-join: continue to parent (outer loop)\n * - Flipped join: stop (already unlimited when it was flipped)\n * - Fan-out/Fan-in: propagate to all inputs\n */\n propagateUnlimit(): void {\n assert(this.#type === 'flipped', 'Can only unlimit a flipped join');\n // Parent stays limited; child becomes unlimited\n this.#child.propagateUnlimitFromFlippedJoin(); // Up the child chain\n }\n\n /**\n * Called when a parent join is flipped and this join is part of its child subgraph.\n * Continue propagation to parent (the outer loop).\n * If we are hitting a semi-join, the parent drives.\n * If we are hitting a flip-join, well now we have to unlimit its parent too!\n */\n propagateUnlimitFromFlippedJoin(): void {\n this.#parent.propagateUnlimitFromFlippedJoin();\n }\n\n propagateConstraints(\n branchPattern: number[],\n constraint: PlannerConstraint | undefined,\n from?: PlannerNode,\n planDebugger?: PlanDebugger,\n ): void {\n planDebugger?.log({\n type: 'node-constraint',\n nodeType: 'join',\n node: this.getName(),\n branchPattern,\n constraint,\n from: from ? getNodeName(from) : 'unknown',\n });\n\n if (this.#type === 'semi') {\n // A semi-join always has constraints for its child.\n // They are defined by the correlation between parent and child.\n this.#child.propagateConstraints(\n branchPattern,\n this.#childConstraint,\n this,\n planDebugger,\n );\n // A semi-join forwards constraints to its parent.\n this.#parent.propagateConstraints(\n branchPattern,\n constraint,\n this,\n planDebugger,\n );\n } else if (this.#type === 'flipped') {\n // A flipped join translates constraints from parent space to child space.\n // This matches FlippedJoin.fetch() runtime behavior where parent constraints\n // on join keys are translated to child constraints.\n // Example: If parent has {issueID: 5} and join maps issueID→id,\n // child gets {id: 5} allowing index usage.\n const translatedConstraint = translateConstraintsForFlippedJoin(\n constraint,\n this.#parentConstraint,\n this.#childConstraint,\n );\n this.#child.propagateConstraints(\n branchPattern,\n translatedConstraint,\n this,\n planDebugger,\n );\n // A flipped join will have constraints to send to its parent.\n // - The constraints its output sent\n // - The constraints its child creates\n this.#parent.propagateConstraints(\n branchPattern,\n mergeConstraints(constraint, this.#parentConstraint),\n this,\n planDebugger,\n );\n }\n }\n\n reset(): void {\n this.#type = this.#initialType;\n }\n\n estimateCost(\n /**\n * This argument is to deal with consecutive `andExists` statements.\n * Each one will constrain how often a parent row passes all constraints.\n * This means that we have to scan more and more parent rows the more\n * constraints we add.\n *\n * DownstreamChildSelectivity factors in fanout factor\n * from parent -> child\n */\n downstreamChildSelectivity: number,\n /**\n * branchPattern uniquely identifies OR branches in the graph.\n * Each path through an OR will have unique constraints to apply to the source\n * connection.\n * branchPattern allows us to correlate a path through the graph\n * to the constraints that should be applied for that path.\n *\n * Example graph:\n * UFO\n * / \\\n * J1 J2\n * \\ /\n * UFI\n *\n * J1 and J2 are joins inside an OR (FO).\n * branchPattern [0] = path through J1\n * branchPattern [1] = path through J2\n *\n * If many ORs are nested, branchPattern will have multiple elements\n * representing each level of OR.\n *\n * If no joins are flipped within the `OR`, then only a single\n * branchPattern element will be needed, as FO represents all sub-joins\n * as a single path.\n */\n branchPattern: number[],\n planDebugger?: PlanDebugger,\n ): CostEstimate {\n /**\n * downstreamChildSelectivity accumulates up a parent chain, not\n * up child chains. Child chains represent independent sub-graphs.\n * So we pass 1 for `downstreamChildSelectivity` when estimating child cost.\n * Put another way, downstreamChildSelectivity impacts how many parent\n * rows are returned.\n */\n const child = this.#child.estimateCost(1, branchPattern, planDebugger);\n\n const fanoutFactor = child.fanout(Object.keys(this.#childConstraint));\n // Factor in how many child rows match a parent row.\n // E.g., if an issue has 10 comments on average then we're more\n // likely to hit a comment compared to if an issue has 1 comment on average.\n // If an index is all nulls (no parents match any children)\n // this will collapse to 0.\n const scaledChildSelectivity =\n 1 - Math.pow(1 - child.selectivity, fanoutFactor.fanout);\n\n // Why do we not need fanout in the other direction?\n // E.g., for an `inventory -> film` flipped-join, if each film has 100 inventories (100 copies)\n // then we're more likely to hit an inventory row compared to if each film has 1 inventory.\n // Flipped-join already accounts for this because the child selectivity is implicitly accounted\n // for. The returned row estimate of the child is already representative of how many\n // rows the child will have post-filtering.\n\n /**\n * How selective is the graph from this point forward?\n * If we are _very_ selective then we must scan more parent rows\n * before finding a match.\n * E.g., if childSelectivity = 0.1 and downstreamChildSelectivity = 0.5\n * then we only pass 5% of parent rows (0.1 * 0.5 = 0.05).\n *\n * This is used to estimate how many rows will be pulled from the parent\n * when trying to satisfy downstream constraints and a limit.\n *\n * NOTE: We do not know if the probabilities are correlated so we assume independence.\n * This is a fundamental limitation of the planner.\n */\n const parent = this.#parent.estimateCost(\n // Selectivity flows up the graph from child to parent\n // so we can determine the total selectivity of all ANDed exists checks.\n this.#type === 'flipped'\n ? 1 * downstreamChildSelectivity\n : scaledChildSelectivity * downstreamChildSelectivity,\n branchPattern,\n planDebugger,\n );\n\n let costEstimate: CostEstimate;\n\n if (this.type === 'semi') {\n costEstimate = {\n startupCost: parent.startupCost,\n scanEst:\n parent.limit === undefined\n ? parent.returnedRows\n : Math.min(\n parent.returnedRows,\n downstreamChildSelectivity === 0\n ? 0\n : parent.limit / downstreamChildSelectivity,\n ),\n cost:\n parent.cost +\n parent.scanEst * (child.startupCost + child.cost + child.scanEst),\n returnedRows: parent.returnedRows * child.selectivity,\n selectivity: child.selectivity * parent.selectivity,\n limit: parent.limit,\n fanout: parent.fanout,\n };\n } else {\n costEstimate = {\n startupCost: child.startupCost,\n scanEst:\n parent.limit === undefined\n ? parent.returnedRows * child.returnedRows\n : Math.min(\n parent.returnedRows * child.returnedRows,\n downstreamChildSelectivity === 0\n ? 0\n : parent.limit / downstreamChildSelectivity,\n ),\n // FlippedJoin batches child→parent lookups into chunks of\n // getMultiConstraintChunkSize(), issuing one IN-list query per\n // chunk. So `parent.startupCost` (statement prepare + plan\n // setup) is paid once per chunk, not once per child row. The\n // per-seek work (`parent.cost` index walk + `parent.scanEst`\n // rows) still scales with child row count: each IN value still\n // does its own index seek.\n cost:\n child.cost +\n Math.ceil(child.scanEst / getMultiConstraintChunkSize()) *\n parent.startupCost +\n child.scanEst * (parent.cost + parent.scanEst),\n // the child selectivity is not relevant here because it has already been taken into account via the flipping.\n // I.e., `child.returnedRows` is the estimated number of rows produced by the child _after_ taking filtering into account.\n returnedRows: parent.returnedRows * child.returnedRows,\n selectivity: parent.selectivity * child.selectivity,\n limit: parent.limit,\n fanout: parent.fanout,\n };\n }\n\n if (planDebugger) {\n planDebugger.log({\n type: 'node-cost',\n nodeType: 'join',\n node: this.getName(),\n branchPattern,\n downstreamChildSelectivity,\n costEstimate: omitFanout(costEstimate),\n joinType: this.#type,\n });\n }\n\n return costEstimate;\n }\n\n /**\n * Get a human-readable name for this join for debugging.\n * Format: \"parentName ⋈ childName\"\n */\n getName(): string {\n const parentName = getNodeName(this.#parent);\n const childName = getNodeName(this.#child);\n return `${parentName} ⋈ ${childName}`;\n }\n\n /**\n * Get debug information about this join's state.\n */\n getDebugInfo(): {\n name: string;\n type: 'semi' | 'flipped';\n planId: number;\n } {\n return {\n name: this.getName(),\n type: this.#type,\n planId: this.planId,\n };\n }\n}\n\nexport class UnflippableJoinError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'UnflippableJoinError';\n }\n}\n\n/**\n * Get a human-readable name for any planner node.\n * Used for debugging and tracing.\n */\nfunction getNodeName(node: PlannerNode): string {\n switch (node.kind) {\n case 'connection':\n return node.name;\n case 'join':\n return node.getName();\n case 'fan-out':\n return 'FO';\n case 'fan-in':\n return 'FI';\n case 'filter':\n return 'filter';\n case 'terminus':\n return 'terminus';\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"planner-join.js","names":[],"sources":["../../../../../zql/src/planner/planner-join.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {getMultiConstraintChunkSize} from '../ivm/flipped-join.ts';\nimport {\n mergeConstraints,\n type PlannerConstraint,\n} from './planner-constraint.ts';\nimport type {PlanDebugger} from './planner-debug.ts';\nimport {omitFanout} from './planner-node.ts';\nimport type {\n CostEstimate,\n JoinOrConnection,\n PlannerNode,\n} from './planner-node.ts';\nimport type {PlannerTerminus} from './planner-terminus.ts';\n\n/**\n * Translate constraints for a flipped join from parent space to child space.\n * Matches the runtime behavior of FlippedJoin.fetch() which translates\n * parent constraints to child constraints using index-based key mapping.\n *\n * Example:\n * parentConstraint = {issueID: undefined, projectID: undefined}\n * childConstraint = {id: undefined, projectID: undefined}\n * incomingConstraint = {issueID: 5}\n * result = {id: 5} // issueID at index 0 maps to id at index 0\n */\nfunction translateConstraintsForFlippedJoin(\n incomingConstraint: PlannerConstraint | undefined,\n parentConstraint: PlannerConstraint,\n childConstraint: PlannerConstraint,\n): PlannerConstraint | undefined {\n if (!incomingConstraint) return undefined;\n\n const parentKeys = Object.keys(parentConstraint);\n const childKeys = Object.keys(childConstraint);\n const translated: PlannerConstraint = {};\n\n for (const [key, value] of Object.entries(incomingConstraint)) {\n const index = parentKeys.indexOf(key);\n if (index !== -1) {\n // Found this key in parent at position `index`\n // Map to child key at same position\n translated[childKeys[index]] = value;\n }\n }\n\n return Object.keys(translated).length > 0 ? translated : undefined;\n}\n\n/**\n * Semi-join overhead multiplier.\n *\n * Semi-joins represent correlated subqueries (EXISTS checks) which have\n * execution overhead compared to flipped joins, even when logical row counts\n * are identical. This overhead comes from:\n * - Need to execute a separate correlation check for each parent row\n * - Cannot leverage combined constraint checking as effectively as flipped joins\n *\n * A multiplier of 1.5 means semi-joins are estimated to be ~50% more expensive\n * than equivalent flipped joins, which empirically matches observed performance\n * differences in production workloads (e.g., 1.7x in zbugs benchmarks).\n *\n * Flipped joins have a different overhead in that they become unlimited. This\n * is accounted for when propagating unlimits rather than here.\n */\n// const SEMI_JOIN_OVERHEAD_MULTIPLIER = 1.5;\n\n/**\n * Represents a join between two data streams (parent and child).\n *\n * # Dual-State Pattern\n * Like all planner nodes, PlannerJoin separates:\n * 1. IMMUTABLE STRUCTURE: Parent/child nodes, constraints, flippability\n * 2. MUTABLE STATE: Join type (semi/flipped), pinned status\n *\n * # Join Flipping\n * A join can be in two states:\n * - 'semi': Parent is outer loop, child is inner (semi-join for EXISTS)\n * - 'flipped': Child is outer loop, parent is inner\n *\n * Flipping is the key optimization: choosing which table scans first.\n * NOT EXISTS joins cannot be flipped (#flippable = false).\n *\n * # Constraint Propagation\n * - Semi-join: Sends childConstraint to child, forwards received constraints to parent\n * - Flipped join: Sends undefined to child, merges parentConstraint with received to parent\n * - Unpinned join: Only forwards constraints to parent (doesn't constrain child yet)\n *\n * # Lifecycle\n * 1. Construct with immutable structure (parent, child, constraints, flippability)\n * 2. Wire to output node during graph construction\n * 3. Planning calls flipIfNeeded() based on connection selection order\n * 4. pin() locks the join type once chosen\n * 5. reset() clears mutable state (type → 'semi', pinned → false)\n */\nexport class PlannerJoin {\n readonly kind = 'join' as const;\n\n readonly #parent: Exclude<PlannerNode, PlannerTerminus>;\n readonly #child: Exclude<PlannerNode, PlannerTerminus>;\n readonly #parentConstraint: PlannerConstraint;\n readonly #childConstraint: PlannerConstraint;\n readonly #flippable: boolean;\n readonly planId: number;\n #output?: PlannerNode | undefined; // Set once during graph construction\n\n // Reset between planning attempts\n #type: 'semi' | 'flipped';\n readonly #initialType: 'semi' | 'flipped';\n\n constructor(\n parent: Exclude<PlannerNode, PlannerTerminus>,\n child: Exclude<PlannerNode, PlannerTerminus>,\n parentConstraint: PlannerConstraint,\n childConstraint: PlannerConstraint,\n flippable: boolean,\n planId: number,\n initialType: 'semi' | 'flipped' = 'semi',\n ) {\n this.#type = initialType;\n this.#initialType = initialType;\n this.#parent = parent;\n this.#child = child;\n this.#childConstraint = childConstraint;\n this.#parentConstraint = parentConstraint;\n this.#flippable = flippable;\n this.planId = planId;\n }\n\n setOutput(node: PlannerNode): void {\n this.#output = node;\n }\n\n get output(): PlannerNode {\n assert(this.#output !== undefined, 'Output not set');\n return this.#output;\n }\n\n get parent(): Exclude<PlannerNode, PlannerTerminus> {\n return this.#parent;\n }\n\n get child(): Exclude<PlannerNode, PlannerTerminus> {\n return this.#child;\n }\n\n closestJoinOrSource(): JoinOrConnection {\n return 'join';\n }\n\n flipIfNeeded(input: PlannerNode): void {\n if (input === this.#child) {\n this.flip();\n } else {\n assert(\n input === this.#parent,\n 'Can only flip a join from one of its inputs',\n );\n }\n }\n\n flip(): void {\n assert(this.#type === 'semi', 'Can only flip a semi-join');\n if (!this.#flippable) {\n throw new UnflippableJoinError(\n 'Cannot flip a non-flippable join (e.g., NOT EXISTS)',\n );\n }\n this.#type = 'flipped';\n }\n\n get type(): 'semi' | 'flipped' {\n return this.#type;\n }\n isFlippable(): boolean {\n return this.#flippable;\n }\n\n /**\n * Propagate unlimiting when this join is flipped.\n * When a join is flipped:\n * 1. Child becomes outer loop → produces all rows (unlimited)\n * 2. Parent is fetched once per child row → effectively unlimited\n *\n * Example: If child produces 896 rows, parent is fetched 896 times.\n * Even if each fetch returns 1 row, parent produces 896 total rows.\n *\n * Propagation rules:\n * - Connection: call unlimit()\n * - Semi-join: continue to parent (outer loop)\n * - Flipped join: stop (already unlimited when it was flipped)\n * - Fan-out/Fan-in: propagate to all inputs\n */\n propagateUnlimit(): void {\n assert(this.#type === 'flipped', 'Can only unlimit a flipped join');\n // Parent stays limited; child becomes unlimited\n this.#child.propagateUnlimitFromFlippedJoin(); // Up the child chain\n }\n\n /**\n * Called when a parent join is flipped and this join is part of its child subgraph.\n * Continue propagation to parent (the outer loop).\n * If we are hitting a semi-join, the parent drives.\n * If we are hitting a flip-join, well now we have to unlimit its parent too!\n */\n propagateUnlimitFromFlippedJoin(): void {\n this.#parent.propagateUnlimitFromFlippedJoin();\n }\n\n propagateConstraints(\n branchPattern: number[],\n constraint: PlannerConstraint | undefined,\n from?: PlannerNode,\n planDebugger?: PlanDebugger,\n ): void {\n planDebugger?.log({\n type: 'node-constraint',\n nodeType: 'join',\n node: this.getName(),\n branchPattern,\n constraint,\n from: from ? getNodeName(from) : 'unknown',\n });\n\n if (this.#type === 'semi') {\n // A semi-join always has constraints for its child.\n // They are defined by the correlation between parent and child.\n this.#child.propagateConstraints(\n branchPattern,\n this.#childConstraint,\n this,\n planDebugger,\n );\n // A semi-join forwards constraints to its parent.\n this.#parent.propagateConstraints(\n branchPattern,\n constraint,\n this,\n planDebugger,\n );\n } else if (this.#type === 'flipped') {\n // A flipped join translates constraints from parent space to child space.\n // This matches FlippedJoin.fetch() runtime behavior where parent constraints\n // on join keys are translated to child constraints.\n // Example: If parent has {issueID: 5} and join maps issueID→id,\n // child gets {id: 5} allowing index usage.\n const translatedConstraint = translateConstraintsForFlippedJoin(\n constraint,\n this.#parentConstraint,\n this.#childConstraint,\n );\n this.#child.propagateConstraints(\n branchPattern,\n translatedConstraint,\n this,\n planDebugger,\n );\n // A flipped join will have constraints to send to its parent.\n // - The constraints its output sent\n // - The constraints its child creates\n this.#parent.propagateConstraints(\n branchPattern,\n mergeConstraints(constraint, this.#parentConstraint),\n this,\n planDebugger,\n );\n }\n }\n\n reset(): void {\n this.#type = this.#initialType;\n }\n\n estimateCost(\n /**\n * This argument is to deal with consecutive `andExists` statements.\n * Each one will constrain how often a parent row passes all constraints.\n * This means that we have to scan more and more parent rows the more\n * constraints we add.\n *\n * DownstreamChildSelectivity factors in fanout factor\n * from parent -> child\n */\n downstreamChildSelectivity: number,\n /**\n * branchPattern uniquely identifies OR branches in the graph.\n * Each path through an OR will have unique constraints to apply to the source\n * connection.\n * branchPattern allows us to correlate a path through the graph\n * to the constraints that should be applied for that path.\n *\n * Example graph:\n * UFO\n * / \\\n * J1 J2\n * \\ /\n * UFI\n *\n * J1 and J2 are joins inside an OR (FO).\n * branchPattern [0] = path through J1\n * branchPattern [1] = path through J2\n *\n * If many ORs are nested, branchPattern will have multiple elements\n * representing each level of OR.\n *\n * If no joins are flipped within the `OR`, then only a single\n * branchPattern element will be needed, as FO represents all sub-joins\n * as a single path.\n */\n branchPattern: number[],\n planDebugger?: PlanDebugger,\n ): CostEstimate {\n /**\n * downstreamChildSelectivity accumulates up a parent chain, not\n * up child chains. Child chains represent independent sub-graphs.\n * So we pass 1 for `downstreamChildSelectivity` when estimating child cost.\n * Put another way, downstreamChildSelectivity impacts how many parent\n * rows are returned.\n */\n const child = this.#child.estimateCost(1, branchPattern, planDebugger);\n\n const fanoutFactor = child.fanout(Object.keys(this.#childConstraint));\n // Factor in how many child rows match a parent row.\n // E.g., if an issue has 10 comments on average then we're more\n // likely to hit a comment compared to if an issue has 1 comment on average.\n // If an index is all nulls (no parents match any children)\n // this will collapse to 0.\n const scaledChildSelectivity =\n 1 - Math.pow(1 - child.selectivity, fanoutFactor.fanout);\n\n // Why do we not need fanout in the other direction?\n // E.g., for an `inventory -> film` flipped-join, if each film has 100 inventories (100 copies)\n // then we're more likely to hit an inventory row compared to if each film has 1 inventory.\n // Flipped-join already accounts for this because the child selectivity is implicitly accounted\n // for. The returned row estimate of the child is already representative of how many\n // rows the child will have post-filtering.\n\n /**\n * How selective is the graph from this point forward?\n * If we are _very_ selective then we must scan more parent rows\n * before finding a match.\n * E.g., if childSelectivity = 0.1 and downstreamChildSelectivity = 0.5\n * then we only pass 5% of parent rows (0.1 * 0.5 = 0.05).\n *\n * This is used to estimate how many rows will be pulled from the parent\n * when trying to satisfy downstream constraints and a limit.\n *\n * NOTE: We do not know if the probabilities are correlated so we assume independence.\n * This is a fundamental limitation of the planner.\n */\n const parent = this.#parent.estimateCost(\n // Selectivity flows up the graph from child to parent\n // so we can determine the total selectivity of all ANDed exists checks.\n this.#type === 'flipped'\n ? 1 * downstreamChildSelectivity\n : scaledChildSelectivity * downstreamChildSelectivity,\n branchPattern,\n planDebugger,\n );\n\n let costEstimate: CostEstimate;\n\n if (this.type === 'semi') {\n costEstimate = {\n startupCost: parent.startupCost,\n scanEst:\n parent.limit === undefined\n ? parent.returnedRows\n : Math.min(\n parent.returnedRows,\n downstreamChildSelectivity === 0\n ? 0\n : parent.limit / downstreamChildSelectivity,\n ),\n cost:\n parent.cost +\n parent.scanEst * (child.startupCost + child.cost + child.scanEst),\n returnedRows: parent.returnedRows * child.selectivity,\n selectivity: child.selectivity * parent.selectivity,\n limit: parent.limit,\n fanout: parent.fanout,\n };\n } else {\n costEstimate = {\n startupCost: child.startupCost,\n scanEst:\n parent.limit === undefined\n ? parent.returnedRows * child.returnedRows\n : Math.min(\n parent.returnedRows * child.returnedRows,\n downstreamChildSelectivity === 0\n ? 0\n : parent.limit / downstreamChildSelectivity,\n ),\n // FlippedJoin batches child→parent lookups into chunks of\n // getMultiConstraintChunkSize(), issuing one IN-list query per\n // chunk. So `parent.startupCost` (statement prepare + plan\n // setup) is paid once per chunk, not once per child row. The\n // per-seek work (`parent.cost` index walk + `parent.scanEst`\n // rows) still scales with child row count: each IN value still\n // does its own index seek.\n cost:\n child.cost +\n Math.ceil(child.scanEst / getMultiConstraintChunkSize()) *\n parent.startupCost +\n child.scanEst * (parent.cost + parent.scanEst),\n // the child selectivity is not relevant here because it has already been taken into account via the flipping.\n // I.e., `child.returnedRows` is the estimated number of rows produced by the child _after_ taking filtering into account.\n returnedRows: parent.returnedRows * child.returnedRows,\n selectivity: parent.selectivity * child.selectivity,\n limit: parent.limit,\n fanout: parent.fanout,\n };\n }\n\n if (planDebugger) {\n planDebugger.log({\n type: 'node-cost',\n nodeType: 'join',\n node: this.getName(),\n branchPattern,\n downstreamChildSelectivity,\n costEstimate: omitFanout(costEstimate),\n joinType: this.#type,\n });\n }\n\n return costEstimate;\n }\n\n /**\n * Get a human-readable name for this join for debugging.\n * Format: \"parentName ⋈ childName\"\n */\n getName(): string {\n const parentName = getNodeName(this.#parent);\n const childName = getNodeName(this.#child);\n return `${parentName} ⋈ ${childName}`;\n }\n\n /**\n * Get debug information about this join's state.\n */\n getDebugInfo(): {\n name: string;\n type: 'semi' | 'flipped';\n planId: number;\n } {\n return {\n name: this.getName(),\n type: this.#type,\n planId: this.planId,\n };\n }\n}\n\nexport class UnflippableJoinError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'UnflippableJoinError';\n }\n}\n\n/**\n * Get a human-readable name for any planner node.\n * Used for debugging and tracing.\n */\nfunction getNodeName(node: PlannerNode): string {\n switch (node.kind) {\n case 'connection':\n return node.name;\n case 'join':\n return node.getName();\n case 'fan-out':\n return 'FO';\n case 'fan-in':\n return 'FI';\n case 'filter':\n return 'filter';\n case 'terminus':\n return 'terminus';\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA0BA,SAAS,mCACP,oBACA,kBACA,iBAC+B;CAC/B,IAAI,CAAC,oBAAoB,OAAO,KAAA;CAEhC,MAAM,aAAa,OAAO,KAAK,gBAAgB;CAC/C,MAAM,YAAY,OAAO,KAAK,eAAe;CAC7C,MAAM,aAAgC,CAAC;CAEvC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,kBAAkB,GAAG;EAC7D,MAAM,QAAQ,WAAW,QAAQ,GAAG;EACpC,IAAI,UAAU,IAGZ,WAAW,UAAU,UAAU;CAEnC;CAEA,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,IAAI,aAAa,KAAA;AAC3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,IAAa,cAAb,MAAyB;CACvB,OAAgB;CAEhB;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CAEA,YACE,QACA,OACA,kBACA,iBACA,WACA,QACA,cAAkC,QAClC;EACA,KAAK,QAAQ;EACb,KAAK,eAAe;EACpB,KAAK,UAAU;EACf,KAAK,SAAS;EACd,KAAK,mBAAmB;EACxB,KAAK,oBAAoB;EACzB,KAAK,aAAa;EAClB,KAAK,SAAS;CAChB;CAEA,UAAU,MAAyB;EACjC,KAAK,UAAU;CACjB;CAEA,IAAI,SAAsB;EACxB,OAAO,KAAK,YAAY,KAAA,GAAW,gBAAgB;EACnD,OAAO,KAAK;CACd;CAEA,IAAI,SAAgD;EAClD,OAAO,KAAK;CACd;CAEA,IAAI,QAA+C;EACjD,OAAO,KAAK;CACd;CAEA,sBAAwC;EACtC,OAAO;CACT;CAEA,aAAa,OAA0B;EACrC,IAAI,UAAU,KAAK,QACjB,KAAK,KAAK;OAEV,OACE,UAAU,KAAK,SACf,6CACF;CAEJ;CAEA,OAAa;EACX,OAAO,KAAK,UAAU,QAAQ,2BAA2B;EACzD,IAAI,CAAC,KAAK,YACR,MAAM,IAAI,qBACR,qDACF;EAEF,KAAK,QAAQ;CACf;CAEA,IAAI,OAA2B;EAC7B,OAAO,KAAK;CACd;CACA,cAAuB;EACrB,OAAO,KAAK;CACd;;;;;;;;;;;;;;;;CAiBA,mBAAyB;EACvB,OAAO,KAAK,UAAU,WAAW,iCAAiC;EAElE,KAAK,OAAO,gCAAgC;CAC9C;;;;;;;CAQA,kCAAwC;EACtC,KAAK,QAAQ,gCAAgC;CAC/C;CAEA,qBACE,eACA,YACA,MACA,cACM;EACN,cAAc,IAAI;GAChB,MAAM;GACN,UAAU;GACV,MAAM,KAAK,QAAQ;GACnB;GACA;GACA,MAAM,OAAO,YAAY,IAAI,IAAI;EACnC,CAAC;EAED,IAAI,KAAK,UAAU,QAAQ;GAGzB,KAAK,OAAO,qBACV,eACA,KAAK,kBACL,MACA,YACF;GAEA,KAAK,QAAQ,qBACX,eACA,YACA,MACA,YACF;EACF,OAAO,IAAI,KAAK,UAAU,WAAW;GAMnC,MAAM,uBAAuB,mCAC3B,YACA,KAAK,mBACL,KAAK,gBACP;GACA,KAAK,OAAO,qBACV,eACA,sBACA,MACA,YACF;GAIA,KAAK,QAAQ,qBACX,eACA,iBAAiB,YAAY,KAAK,iBAAiB,GACnD,MACA,YACF;EACF;CACF;CAEA,QAAc;EACZ,KAAK,QAAQ,KAAK;CACpB;CAEA,aAUE,4BA0BA,eACA,cACc;;;;;;;;EAQd,MAAM,QAAQ,KAAK,OAAO,aAAa,GAAG,eAAe,YAAY;EAErE,MAAM,eAAe,MAAM,OAAO,OAAO,KAAK,KAAK,gBAAgB,CAAC;EAMpE,MAAM,yBACJ,IAAI,KAAK,IAAI,IAAI,MAAM,aAAa,aAAa,MAAM;;;;;;;;;;;;;;EAsBzD,MAAM,SAAS,KAAK,QAAQ,aAG1B,KAAK,UAAU,YACX,IAAI,6BACJ,yBAAyB,4BAC7B,eACA,YACF;EAEA,IAAI;EAEJ,IAAI,KAAK,SAAS,QAChB,eAAe;GACb,aAAa,OAAO;GACpB,SACE,OAAO,UAAU,KAAA,IACb,OAAO,eACP,KAAK,IACH,OAAO,cACP,+BAA+B,IAC3B,IACA,OAAO,QAAQ,0BACrB;GACN,MACE,OAAO,OACP,OAAO,WAAW,MAAM,cAAc,MAAM,OAAO,MAAM;GAC3D,cAAc,OAAO,eAAe,MAAM;GAC1C,aAAa,MAAM,cAAc,OAAO;GACxC,OAAO,OAAO;GACd,QAAQ,OAAO;EACjB;OAEA,eAAe;GACb,aAAa,MAAM;GACnB,SACE,OAAO,UAAU,KAAA,IACb,OAAO,eAAe,MAAM,eAC5B,KAAK,IACH,OAAO,eAAe,MAAM,cAC5B,+BAA+B,IAC3B,IACA,OAAO,QAAQ,0BACrB;GAQN,MACE,MAAM,OACN,KAAK,KAAK,MAAM,UAAU,4BAA4B,CAAC,IACrD,OAAO,cACT,MAAM,WAAW,OAAO,OAAO,OAAO;GAGxC,cAAc,OAAO,eAAe,MAAM;GAC1C,aAAa,OAAO,cAAc,MAAM;GACxC,OAAO,OAAO;GACd,QAAQ,OAAO;EACjB;EAGF,IAAI,cACF,aAAa,IAAI;GACf,MAAM;GACN,UAAU;GACV,MAAM,KAAK,QAAQ;GACnB;GACA;GACA,cAAc,WAAW,YAAY;GACrC,UAAU,KAAK;EACjB,CAAC;EAGH,OAAO;CACT;;;;;CAMA,UAAkB;EAGhB,OAAO,GAFY,YAAY,KAAK,OAE1B,EAAW,KADH,YAAY,KAAK,MACT;CAC5B;;;;CAKA,eAIE;EACA,OAAO;GACL,MAAM,KAAK,QAAQ;GACnB,MAAM,KAAK;GACX,QAAQ,KAAK;EACf;CACF;AACF;AAEA,IAAa,uBAAb,cAA0C,MAAM;CAC9C,YAAY,SAAiB;EAC3B,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;;;;;AAMA,SAAS,YAAY,MAA2B;CAC9C,QAAQ,KAAK,MAAb;EACE,KAAK,cACH,OAAO,KAAK;EACd,KAAK,QACH,OAAO,KAAK,QAAQ;EACtB,KAAK,WACH,OAAO;EACT,KAAK,UACH,OAAO;EACT,KAAK,UACH,OAAO;EACT,KAAK,YACH,OAAO;CACX;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-source.js","names":[
|
|
1
|
+
{"version":3,"file":"planner-source.js","names":[],"sources":["../../../../../zql/src/planner/planner-source.ts"],"sourcesContent":["import type {\n Condition,\n Ordering,\n SimpleCondition,\n} from '../../../zero-protocol/src/ast.ts';\nimport {\n PlannerConnection,\n type ConnectionCostModel,\n} from './planner-connection.ts';\nimport type {PlannerConstraint} from './planner-constraint.ts';\n\nexport type {ConnectionCostModel};\n\nexport class PlannerSource {\n readonly name: string;\n readonly #model: ConnectionCostModel;\n\n constructor(name: string, model: ConnectionCostModel) {\n this.name = name;\n this.#model = model;\n }\n\n connect(\n sort: Ordering,\n filters: Condition | undefined,\n isRoot: boolean,\n baseConstraints?: PlannerConstraint,\n limit?: number,\n pushed?: ReadonlySet<SimpleCondition>,\n ): PlannerConnection {\n return new PlannerConnection(\n this.name,\n this.#model,\n sort,\n filters,\n isRoot,\n baseConstraints,\n limit,\n undefined,\n pushed,\n );\n }\n}\n"],"mappings":";;AAaA,IAAa,gBAAb,MAA2B;CACzB;CACA;CAEA,YAAY,MAAc,OAA4B;EACpD,KAAK,OAAO;EACZ,KAAK,SAAS;CAChB;CAEA,QACE,MACA,SACA,QACA,iBACA,OACA,QACmB;EACnB,OAAO,IAAI,kBACT,KAAK,MACL,KAAK,QACL,MACA,SACA,QACA,iBACA,OACA,KAAA,GACA,MACF;CACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-terminus.js","names":[
|
|
1
|
+
{"version":3,"file":"planner-terminus.js","names":[],"sources":["../../../../../zql/src/planner/planner-terminus.ts"],"sourcesContent":["import type {PlanDebugger} from './planner-debug.ts';\nimport type {\n CostEstimate,\n JoinOrConnection,\n PlannerNode,\n} from './planner-node.ts';\n\nexport class PlannerTerminus {\n readonly kind = 'terminus' as const;\n readonly #input: Exclude<PlannerNode, PlannerTerminus>;\n\n constructor(input: Exclude<PlannerNode, PlannerTerminus>) {\n this.#input = input;\n }\n\n get pinned(): boolean {\n return true;\n }\n\n closestJoinOrSource(): JoinOrConnection {\n return this.#input.closestJoinOrSource();\n }\n\n propagateConstraints(planDebugger?: PlanDebugger): void {\n this.#input.propagateConstraints([], undefined, this, planDebugger);\n }\n\n estimateCost(planDebugger?: PlanDebugger): CostEstimate {\n // Terminus starts the cost estimation flow with empty branch pattern\n return this.#input.estimateCost(1, [], planDebugger);\n }\n\n /**\n * Propagate unlimiting when a parent join is flipped.\n * Terminus doesn't participate in unlimiting.\n */\n propagateUnlimitFromFlippedJoin(): void {\n // No-op: terminus doesn't need to unlimit anything\n }\n}\n"],"mappings":";AAOA,IAAa,kBAAb,MAA6B;CAC3B,OAAgB;CAChB;CAEA,YAAY,OAA8C;EACxD,KAAK,SAAS;CAChB;CAEA,IAAI,SAAkB;EACpB,OAAO;CACT;CAEA,sBAAwC;EACtC,OAAO,KAAK,OAAO,oBAAoB;CACzC;CAEA,qBAAqB,cAAmC;EACtD,KAAK,OAAO,qBAAqB,CAAC,GAAG,KAAA,GAAW,MAAM,YAAY;CACpE;CAEA,aAAa,cAA2C;EAEtD,OAAO,KAAK,OAAO,aAAa,GAAG,CAAC,GAAG,YAAY;CACrD;;;;;CAMA,kCAAwC,CAExC;AACF"}
|
|
@@ -14,7 +14,8 @@ function createRunnableBuilder(delegate, schema) {
|
|
|
14
14
|
return createBuilderWithQueryFactory(schema, (table) => newRunnableQuery(delegate, schema, table));
|
|
15
15
|
}
|
|
16
16
|
function createBuilderWithQueryFactory(schema, queryFactory) {
|
|
17
|
-
|
|
17
|
+
const target = Object.assign(Object.create(null), schema.tables);
|
|
18
|
+
return recordProxy(target, (_tableSchema, prop) => queryFactory(prop));
|
|
18
19
|
}
|
|
19
20
|
//#endregion
|
|
20
21
|
export { createBuilder, createRunnableBuilder };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-builder.js","names":[],"sources":["../../../../../zql/src/query/create-builder.ts"],"sourcesContent":["import {recordProxy} from '../../../shared/src/record-proxy.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport type {QueryDelegate} from './query-delegate.ts';\nimport {newQuery} from './query-impl.ts';\nimport type {Query} from './query.ts';\nimport {newRunnableQuery} from './runnable-query-impl.ts';\nimport type {ConditionalSchemaQuery, SchemaQuery} from './schema-query.ts';\n\n/**\n * Returns a set of query builders for the given schema.\n */\nexport function createBuilder<S extends Schema>(schema: S): SchemaQuery<S> {\n return createBuilderWithQueryFactory(schema, table =>\n newQuery(schema, table),\n );\n}\n\n/** @deprecated Use {@link createBuilder} with `tx.run(zql.table.where(...))` instead. */\nexport function createRunnableBuilder<S extends Schema>(\n delegate: QueryDelegate,\n schema: S,\n): ConditionalSchemaQuery<S> {\n if (!schema.enableLegacyQueries) {\n return undefined as ConditionalSchemaQuery<S>;\n }\n\n return createBuilderWithQueryFactory(schema, table =>\n newRunnableQuery(delegate, schema, table),\n ) as ConditionalSchemaQuery<S>;\n}\n\nfunction createBuilderWithQueryFactory<S extends Schema>(\n schema: S,\n queryFactory: (table: keyof S['tables'] & string) => Query<string, S>,\n): SchemaQuery<S> {\n // Create a target with no prototype so accessing unknown properties returns\n // undefined instead of inherited Object.prototype methods (e.g., toString).\n // This fixes React 19 dev mode compatibility where accessing $$typeof should\n // return undefined rather than throwing.\n const target = Object.assign(Object.create(null), schema.tables) as Record<\n string,\n unknown\n >;\n\n // No onMissing handler needed - unknown properties return undefined due to\n // null prototype, which is the desired behavior for normal JS object semantics.\n return recordProxy(target, (_tableSchema, prop) =>\n queryFactory(prop),\n ) as SchemaQuery<S>;\n}\n"],"mappings":";;;;;;;AAWA,SAAgB,cAAgC,QAA2B;CACzE,OAAO,8BAA8B,SAAQ,UAC3C,SAAS,QAAQ,KAAK,CACxB;AACF;;AAGA,SAAgB,sBACd,UACA,QAC2B;CAC3B,IAAI,CAAC,OAAO,qBACV;CAGF,OAAO,8BAA8B,SAAQ,UAC3C,iBAAiB,UAAU,QAAQ,KAAK,CAC1C;AACF;AAEA,SAAS,8BACP,QACA,cACgB;
|
|
1
|
+
{"version":3,"file":"create-builder.js","names":[],"sources":["../../../../../zql/src/query/create-builder.ts"],"sourcesContent":["import {recordProxy} from '../../../shared/src/record-proxy.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport type {QueryDelegate} from './query-delegate.ts';\nimport {newQuery} from './query-impl.ts';\nimport type {Query} from './query.ts';\nimport {newRunnableQuery} from './runnable-query-impl.ts';\nimport type {ConditionalSchemaQuery, SchemaQuery} from './schema-query.ts';\n\n/**\n * Returns a set of query builders for the given schema.\n */\nexport function createBuilder<S extends Schema>(schema: S): SchemaQuery<S> {\n return createBuilderWithQueryFactory(schema, table =>\n newQuery(schema, table),\n );\n}\n\n/** @deprecated Use {@link createBuilder} with `tx.run(zql.table.where(...))` instead. */\nexport function createRunnableBuilder<S extends Schema>(\n delegate: QueryDelegate,\n schema: S,\n): ConditionalSchemaQuery<S> {\n if (!schema.enableLegacyQueries) {\n return undefined as ConditionalSchemaQuery<S>;\n }\n\n return createBuilderWithQueryFactory(schema, table =>\n newRunnableQuery(delegate, schema, table),\n ) as ConditionalSchemaQuery<S>;\n}\n\nfunction createBuilderWithQueryFactory<S extends Schema>(\n schema: S,\n queryFactory: (table: keyof S['tables'] & string) => Query<string, S>,\n): SchemaQuery<S> {\n // Create a target with no prototype so accessing unknown properties returns\n // undefined instead of inherited Object.prototype methods (e.g., toString).\n // This fixes React 19 dev mode compatibility where accessing $$typeof should\n // return undefined rather than throwing.\n const target = Object.assign(Object.create(null), schema.tables) as Record<\n string,\n unknown\n >;\n\n // No onMissing handler needed - unknown properties return undefined due to\n // null prototype, which is the desired behavior for normal JS object semantics.\n return recordProxy(target, (_tableSchema, prop) =>\n queryFactory(prop),\n ) as SchemaQuery<S>;\n}\n"],"mappings":";;;;;;;AAWA,SAAgB,cAAgC,QAA2B;CACzE,OAAO,8BAA8B,SAAQ,UAC3C,SAAS,QAAQ,KAAK,CACxB;AACF;;AAGA,SAAgB,sBACd,UACA,QAC2B;CAC3B,IAAI,CAAC,OAAO,qBACV;CAGF,OAAO,8BAA8B,SAAQ,UAC3C,iBAAiB,UAAU,QAAQ,KAAK,CAC1C;AACF;AAEA,SAAS,8BACP,QACA,cACgB;CAKhB,MAAM,SAAS,OAAO,OAAO,OAAO,OAAO,IAAI,GAAG,OAAO,MAAM;CAO/D,OAAO,YAAY,SAAS,cAAc,SACxC,aAAa,IAAI,CACnB;AACF"}
|
|
@@ -164,6 +164,6 @@ function filterFalse(conditions) {
|
|
|
164
164
|
return conditions.filter((c) => !isAlwaysFalse(c));
|
|
165
165
|
}
|
|
166
166
|
//#endregion
|
|
167
|
-
export { ExpressionBuilder, and, cmp, simplifyCondition };
|
|
167
|
+
export { ExpressionBuilder, TRUE, and, cmp, flatten, negateOperator, not, or, simplifyCondition };
|
|
168
168
|
|
|
169
169
|
//# sourceMappingURL=expression.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expression.js","names":["#exists"],"sources":["../../../../../zql/src/query/expression.ts"],"sourcesContent":["/* oxlint-disable @typescript-eslint/no-explicit-any */\nimport {must} from '../../../shared/src/must.ts';\nimport {\n toStaticParam,\n type Condition,\n type LiteralValue,\n type Parameter,\n type SimpleOperator,\n} from '../../../zero-protocol/src/ast.ts';\nimport type {Schema as ZeroSchema} from '../../../zero-types/src/schema.ts';\nimport type {\n DestTableName,\n ExistsOptions,\n GetFilterType,\n NoCompoundTypeSelector,\n PullTableSchema,\n Query,\n} from './query.ts';\n\nexport type ParameterReference = {\n [toStaticParam](): Parameter;\n};\n\n/**\n * A factory function that creates a condition. This is used to create\n * complex conditions that can be passed to the `where` method of a query.\n *\n * @example\n *\n * ```ts\n * const condition: ExpressionFactory<User> = ({and, cmp, or}) =>\n * and(\n * cmp('name', '=', 'Alice'),\n * or(cmp('age', '>', 18), cmp('isStudent', '=', true)),\n * );\n *\n * const query = z.query.user.where(condition);\n * ```\n */\nexport interface ExpressionFactory<\n TTable extends keyof TSchema['tables'] & string,\n TSchema extends ZeroSchema,\n> {\n (eb: ExpressionBuilder<TTable, TSchema>): Condition;\n}\n\nexport class ExpressionBuilder<\n TTable extends keyof TSchema['tables'] & string,\n TSchema extends ZeroSchema,\n> {\n readonly #exists: (\n relationship: string,\n cb?: (query: Query<TTable, TSchema>) => Query<TTable, TSchema, any>,\n options?: ExistsOptions,\n ) => Condition;\n\n constructor(\n exists: (\n relationship: string,\n cb?: (query: Query<TTable, TSchema>) => Query<TTable, TSchema, any>,\n options?: ExistsOptions,\n ) => Condition,\n ) {\n this.#exists = exists;\n this.exists = this.exists.bind(this);\n }\n\n get eb() {\n return this;\n }\n\n cmp<\n TSelector extends NoCompoundTypeSelector<PullTableSchema<TTable, TSchema>>,\n TOperator extends SimpleOperator,\n >(\n field: TSelector,\n op: TOperator,\n value:\n | GetFilterType<PullTableSchema<TTable, TSchema>, TSelector, TOperator>\n | ParameterReference\n | undefined,\n ): Condition;\n cmp<\n TSelector extends NoCompoundTypeSelector<PullTableSchema<TTable, TSchema>>,\n >(\n field: TSelector,\n value:\n | GetFilterType<PullTableSchema<TTable, TSchema>, TSelector, '='>\n | ParameterReference\n | undefined,\n ): Condition;\n cmp(\n field: string,\n opOrValue: SimpleOperator | ParameterReference | LiteralValue | undefined,\n value?: ParameterReference | LiteralValue,\n ): Condition {\n if (arguments.length === 2) {\n return cmp(field, opOrValue);\n }\n return cmp(field, opOrValue, value);\n }\n\n cmpLit(\n left: ParameterReference | LiteralValue | undefined,\n op: SimpleOperator,\n right: ParameterReference | LiteralValue | undefined,\n ): Condition {\n return {\n type: 'simple',\n left: isParameterReference(left)\n ? left[toStaticParam]()\n : {type: 'literal', value: left ?? null},\n right: isParameterReference(right)\n ? right[toStaticParam]()\n : {type: 'literal', value: right ?? null},\n op,\n };\n }\n\n and = and;\n or = or;\n not = not;\n\n // The relationship names are inline for the reason given on `Query.related`.\n exists = <\n TRelationship extends keyof TSchema['relationships'][TTable] & string,\n >(\n relationship: TRelationship,\n cb?: (\n query: Query<DestTableName<TTable, TSchema, TRelationship>, TSchema>,\n ) => Query<any, TSchema>,\n options?: ExistsOptions,\n ): Condition => this.#exists(relationship, cb, options);\n}\n\nexport function and(...conditions: (Condition | undefined)[]): Condition {\n const expressions = filterTrue(filterUndefined(conditions));\n\n if (expressions.length === 1) {\n return expressions[0];\n }\n\n if (expressions.some(isAlwaysFalse)) {\n return FALSE;\n }\n\n return {type: 'and', conditions: expressions};\n}\n\nexport function or(...conditions: (Condition | undefined)[]): Condition {\n const expressions = filterFalse(filterUndefined(conditions));\n\n if (expressions.length === 1) {\n return expressions[0];\n }\n\n if (expressions.some(isAlwaysTrue)) {\n return TRUE;\n }\n\n return {type: 'or', conditions: expressions};\n}\n\nexport function not(expression: Condition): Condition {\n switch (expression.type) {\n case 'and':\n return {\n type: 'or',\n conditions: expression.conditions.map(not),\n };\n case 'or':\n return {\n type: 'and',\n conditions: expression.conditions.map(not),\n };\n case 'correlatedSubquery':\n return {\n type: 'correlatedSubquery',\n related: expression.related,\n op: negateOperator(expression.op),\n ...(expression.flip !== undefined ? {flip: expression.flip} : {}),\n ...(expression.scalar !== undefined ? {scalar: expression.scalar} : {}),\n };\n case 'simple':\n return {\n type: 'simple',\n op: negateOperator(expression.op),\n left: expression.left,\n right: expression.right,\n };\n }\n}\n\nexport function cmp(\n field: string,\n opOrValue: SimpleOperator | ParameterReference | LiteralValue | undefined,\n value?: ParameterReference | LiteralValue,\n): Condition {\n let op: SimpleOperator;\n let actualValue: ParameterReference | LiteralValue | undefined;\n\n if (arguments.length === 2) {\n // 2-arg form: cmp(field, value) - defaults to '=' operator\n actualValue = opOrValue as ParameterReference | LiteralValue | undefined;\n op = '=';\n } else {\n // 3-arg form: cmp(field, op, value)\n op = opOrValue as SimpleOperator;\n actualValue = value;\n }\n\n return {\n type: 'simple',\n left: {type: 'column', name: field},\n right: isParameterReference(actualValue)\n ? actualValue[toStaticParam]()\n : {type: 'literal', value: actualValue ?? null},\n op,\n };\n}\n\nfunction isParameterReference(\n value: ParameterReference | LiteralValue | null | undefined,\n): value is ParameterReference {\n return (\n value !== null &&\n value !== undefined &&\n typeof value === 'object' &&\n (value as any)[toStaticParam]\n );\n}\n\nexport const TRUE: Condition = {\n type: 'and',\n conditions: [],\n};\n\nconst FALSE: Condition = {\n type: 'or',\n conditions: [],\n};\n\nfunction isAlwaysTrue(condition: Condition): boolean {\n return condition.type === 'and' && condition.conditions.length === 0;\n}\n\nfunction isAlwaysFalse(condition: Condition): boolean {\n return condition.type === 'or' && condition.conditions.length === 0;\n}\n\nexport function simplifyCondition(c: Condition): Condition {\n if (c.type === 'simple' || c.type === 'correlatedSubquery') {\n return c;\n }\n if (c.conditions.length === 1) {\n return simplifyCondition(c.conditions[0]);\n }\n const conditions = flatten(c.type, c.conditions.map(simplifyCondition));\n if (c.type === 'and' && conditions.some(isAlwaysFalse)) {\n return FALSE;\n }\n if (c.type === 'or' && conditions.some(isAlwaysTrue)) {\n return TRUE;\n }\n return {\n type: c.type,\n conditions,\n };\n}\n\nexport function flatten(\n type: 'and' | 'or',\n conditions: readonly Condition[],\n): Condition[] {\n const flattened: Condition[] = [];\n for (const c of conditions) {\n if (c.type === type) {\n flattened.push(...c.conditions);\n } else {\n flattened.push(c);\n }\n }\n\n return flattened;\n}\n\nconst negateSimpleOperatorMap = {\n ['=']: '!=',\n ['!=']: '=',\n ['<']: '>=',\n ['>']: '<=',\n ['>=']: '<',\n ['<=']: '>',\n ['IN']: 'NOT IN',\n ['NOT IN']: 'IN',\n ['LIKE']: 'NOT LIKE',\n ['NOT LIKE']: 'LIKE',\n ['ILIKE']: 'NOT ILIKE',\n ['NOT ILIKE']: 'ILIKE',\n ['IS']: 'IS NOT',\n ['IS NOT']: 'IS',\n} as const;\n\nconst negateOperatorMap = {\n ...negateSimpleOperatorMap,\n ['EXISTS']: 'NOT EXISTS',\n ['NOT EXISTS']: 'EXISTS',\n} as const;\n\nexport function negateOperator<OP extends keyof typeof negateOperatorMap>(\n op: OP,\n): (typeof negateOperatorMap)[OP] {\n return must(negateOperatorMap[op]);\n}\n\nfunction filterUndefined<T>(array: (T | undefined)[]): T[] {\n return array.filter(e => e !== undefined);\n}\n\nfunction filterTrue(conditions: Condition[]): Condition[] {\n return conditions.filter(c => !isAlwaysTrue(c));\n}\n\nfunction filterFalse(conditions: Condition[]): Condition[] {\n return conditions.filter(c => !isAlwaysFalse(c));\n}\n"],"mappings":";;;AA8CA,IAAa,oBAAb,MAGE;CACA;CAMA,YACE,QAKA;EACA,KAAKA,UAAU;EACf,KAAK,SAAS,KAAK,OAAO,KAAK,IAAI;CACrC;CAEA,IAAI,KAAK;EACP,OAAO;CACT;CAsBA,IACE,OACA,WACA,OACW;EACX,IAAI,UAAU,WAAW,GACvB,OAAO,IAAI,OAAO,SAAS;EAE7B,OAAO,IAAI,OAAO,WAAW,KAAK;CACpC;CAEA,OACE,MACA,IACA,OACW;EACX,OAAO;GACL,MAAM;GACN,MAAM,qBAAqB,IAAI,IAC3B,KAAK,eAAe,IACpB;IAAC,MAAM;IAAW,OAAO,QAAQ;GAAI;GACzC,OAAO,qBAAqB,KAAK,IAC7B,MAAM,eAAe,IACrB;IAAC,MAAM;IAAW,OAAO,SAAS;GAAI;GAC1C;EACF;CACF;CAEA,MAAM;CACN,KAAK;CACL,MAAM;CAGN,UAGE,cACA,IAGA,YACc,KAAKA,QAAQ,cAAc,IAAI,OAAO;AACxD;AAEA,SAAgB,IAAI,GAAG,YAAkD;CACvE,MAAM,cAAc,WAAW,gBAAgB,UAAU,CAAC;CAE1D,IAAI,YAAY,WAAW,GACzB,OAAO,YAAY;CAGrB,IAAI,YAAY,KAAK,aAAa,GAChC,OAAO;CAGT,OAAO;EAAC,MAAM;EAAO,YAAY;CAAW;AAC9C;AAEA,SAAgB,GAAG,GAAG,YAAkD;CACtE,MAAM,cAAc,YAAY,gBAAgB,UAAU,CAAC;CAE3D,IAAI,YAAY,WAAW,GACzB,OAAO,YAAY;CAGrB,IAAI,YAAY,KAAK,YAAY,GAC/B,OAAO;CAGT,OAAO;EAAC,MAAM;EAAM,YAAY;CAAW;AAC7C;AAEA,SAAgB,IAAI,YAAkC;CACpD,QAAQ,WAAW,MAAnB;EACE,KAAK,OACH,OAAO;GACL,MAAM;GACN,YAAY,WAAW,WAAW,IAAI,GAAG;EAC3C;EACF,KAAK,MACH,OAAO;GACL,MAAM;GACN,YAAY,WAAW,WAAW,IAAI,GAAG;EAC3C;EACF,KAAK,sBACH,OAAO;GACL,MAAM;GACN,SAAS,WAAW;GACpB,IAAI,eAAe,WAAW,EAAE;GAChC,GAAI,WAAW,SAAS,KAAA,IAAY,EAAC,MAAM,WAAW,KAAI,IAAI,CAAC;GAC/D,GAAI,WAAW,WAAW,KAAA,IAAY,EAAC,QAAQ,WAAW,OAAM,IAAI,CAAC;EACvE;EACF,KAAK,UACH,OAAO;GACL,MAAM;GACN,IAAI,eAAe,WAAW,EAAE;GAChC,MAAM,WAAW;GACjB,OAAO,WAAW;EACpB;CACJ;AACF;AAEA,SAAgB,IACd,OACA,WACA,OACW;CACX,IAAI;CACJ,IAAI;CAEJ,IAAI,UAAU,WAAW,GAAG;EAE1B,cAAc;EACd,KAAK;CACP,OAAO;EAEL,KAAK;EACL,cAAc;CAChB;CAEA,OAAO;EACL,MAAM;EACN,MAAM;GAAC,MAAM;GAAU,MAAM;EAAK;EAClC,OAAO,qBAAqB,WAAW,IACnC,YAAY,eAAe,IAC3B;GAAC,MAAM;GAAW,OAAO,eAAe;EAAI;EAChD;CACF;AACF;AAEA,SAAS,qBACP,OAC6B;CAC7B,OACE,UAAU,QACV,UAAU,KAAA,KACV,OAAO,UAAU,YAChB,MAAc;AAEnB;AAEA,IAAa,OAAkB;CAC7B,MAAM;CACN,YAAY,CAAC;AACf;AAEA,IAAM,QAAmB;CACvB,MAAM;CACN,YAAY,CAAC;AACf;AAEA,SAAS,aAAa,WAA+B;CACnD,OAAO,UAAU,SAAS,SAAS,UAAU,WAAW,WAAW;AACrE;AAEA,SAAS,cAAc,WAA+B;CACpD,OAAO,UAAU,SAAS,QAAQ,UAAU,WAAW,WAAW;AACpE;AAEA,SAAgB,kBAAkB,GAAyB;CACzD,IAAI,EAAE,SAAS,YAAY,EAAE,SAAS,sBACpC,OAAO;CAET,IAAI,EAAE,WAAW,WAAW,GAC1B,OAAO,kBAAkB,EAAE,WAAW,EAAE;CAE1C,MAAM,aAAa,QAAQ,EAAE,MAAM,EAAE,WAAW,IAAI,iBAAiB,CAAC;CACtE,IAAI,EAAE,SAAS,SAAS,WAAW,KAAK,aAAa,GACnD,OAAO;CAET,IAAI,EAAE,SAAS,QAAQ,WAAW,KAAK,YAAY,GACjD,OAAO;CAET,OAAO;EACL,MAAM,EAAE;EACR;CACF;AACF;AAEA,SAAgB,QACd,MACA,YACa;CACb,MAAM,YAAyB,CAAC;CAChC,KAAK,MAAM,KAAK,YACd,IAAI,EAAE,SAAS,MACb,UAAU,KAAK,GAAG,EAAE,UAAU;MAE9B,UAAU,KAAK,CAAC;CAIpB,OAAO;AACT;AAmBA,IAAM,oBAAoB;EAhBvB,MAAM;EACN,OAAO;EACP,MAAM;EACN,MAAM;EACN,OAAO;EACP,OAAO;EACP,OAAO;EACP,WAAW;EACX,SAAS;EACT,aAAa;EACb,UAAU;EACV,cAAc;EACd,OAAO;EACP,WAAW;EAKX,WAAW;EACX,eAAe;AAClB;AAEA,SAAgB,eACd,IACgC;CAChC,OAAO,KAAK,kBAAkB,GAAG;AACnC;AAEA,SAAS,gBAAmB,OAA+B;CACzD,OAAO,MAAM,QAAO,MAAK,MAAM,KAAA,CAAS;AAC1C;AAEA,SAAS,WAAW,YAAsC;CACxD,OAAO,WAAW,QAAO,MAAK,CAAC,aAAa,CAAC,CAAC;AAChD;AAEA,SAAS,YAAY,YAAsC;CACzD,OAAO,WAAW,QAAO,MAAK,CAAC,cAAc,CAAC,CAAC;AACjD"}
|
|
1
|
+
{"version":3,"file":"expression.js","names":[],"sources":["../../../../../zql/src/query/expression.ts"],"sourcesContent":["/* oxlint-disable @typescript-eslint/no-explicit-any */\nimport {must} from '../../../shared/src/must.ts';\nimport {\n toStaticParam,\n type Condition,\n type LiteralValue,\n type Parameter,\n type SimpleOperator,\n} from '../../../zero-protocol/src/ast.ts';\nimport type {Schema as ZeroSchema} from '../../../zero-types/src/schema.ts';\nimport type {\n DestTableName,\n ExistsOptions,\n GetFilterType,\n NoCompoundTypeSelector,\n PullTableSchema,\n Query,\n} from './query.ts';\n\nexport type ParameterReference = {\n [toStaticParam](): Parameter;\n};\n\n/**\n * A factory function that creates a condition. This is used to create\n * complex conditions that can be passed to the `where` method of a query.\n *\n * @example\n *\n * ```ts\n * const condition: ExpressionFactory<User> = ({and, cmp, or}) =>\n * and(\n * cmp('name', '=', 'Alice'),\n * or(cmp('age', '>', 18), cmp('isStudent', '=', true)),\n * );\n *\n * const query = z.query.user.where(condition);\n * ```\n */\nexport interface ExpressionFactory<\n TTable extends keyof TSchema['tables'] & string,\n TSchema extends ZeroSchema,\n> {\n (eb: ExpressionBuilder<TTable, TSchema>): Condition;\n}\n\nexport class ExpressionBuilder<\n TTable extends keyof TSchema['tables'] & string,\n TSchema extends ZeroSchema,\n> {\n readonly #exists: (\n relationship: string,\n cb?: (query: Query<TTable, TSchema>) => Query<TTable, TSchema, any>,\n options?: ExistsOptions,\n ) => Condition;\n\n constructor(\n exists: (\n relationship: string,\n cb?: (query: Query<TTable, TSchema>) => Query<TTable, TSchema, any>,\n options?: ExistsOptions,\n ) => Condition,\n ) {\n this.#exists = exists;\n this.exists = this.exists.bind(this);\n }\n\n get eb() {\n return this;\n }\n\n cmp<\n TSelector extends NoCompoundTypeSelector<PullTableSchema<TTable, TSchema>>,\n TOperator extends SimpleOperator,\n >(\n field: TSelector,\n op: TOperator,\n value:\n | GetFilterType<PullTableSchema<TTable, TSchema>, TSelector, TOperator>\n | ParameterReference\n | undefined,\n ): Condition;\n cmp<\n TSelector extends NoCompoundTypeSelector<PullTableSchema<TTable, TSchema>>,\n >(\n field: TSelector,\n value:\n | GetFilterType<PullTableSchema<TTable, TSchema>, TSelector, '='>\n | ParameterReference\n | undefined,\n ): Condition;\n cmp(\n field: string,\n opOrValue: SimpleOperator | ParameterReference | LiteralValue | undefined,\n value?: ParameterReference | LiteralValue,\n ): Condition {\n if (arguments.length === 2) {\n return cmp(field, opOrValue);\n }\n return cmp(field, opOrValue, value);\n }\n\n cmpLit(\n left: ParameterReference | LiteralValue | undefined,\n op: SimpleOperator,\n right: ParameterReference | LiteralValue | undefined,\n ): Condition {\n return {\n type: 'simple',\n left: isParameterReference(left)\n ? left[toStaticParam]()\n : {type: 'literal', value: left ?? null},\n right: isParameterReference(right)\n ? right[toStaticParam]()\n : {type: 'literal', value: right ?? null},\n op,\n };\n }\n\n and = and;\n or = or;\n not = not;\n\n // The relationship names are inline for the reason given on `Query.related`.\n exists = <\n TRelationship extends keyof TSchema['relationships'][TTable] & string,\n >(\n relationship: TRelationship,\n cb?: (\n query: Query<DestTableName<TTable, TSchema, TRelationship>, TSchema>,\n ) => Query<any, TSchema>,\n options?: ExistsOptions,\n ): Condition => this.#exists(relationship, cb, options);\n}\n\nexport function and(...conditions: (Condition | undefined)[]): Condition {\n const expressions = filterTrue(filterUndefined(conditions));\n\n if (expressions.length === 1) {\n return expressions[0];\n }\n\n if (expressions.some(isAlwaysFalse)) {\n return FALSE;\n }\n\n return {type: 'and', conditions: expressions};\n}\n\nexport function or(...conditions: (Condition | undefined)[]): Condition {\n const expressions = filterFalse(filterUndefined(conditions));\n\n if (expressions.length === 1) {\n return expressions[0];\n }\n\n if (expressions.some(isAlwaysTrue)) {\n return TRUE;\n }\n\n return {type: 'or', conditions: expressions};\n}\n\nexport function not(expression: Condition): Condition {\n switch (expression.type) {\n case 'and':\n return {\n type: 'or',\n conditions: expression.conditions.map(not),\n };\n case 'or':\n return {\n type: 'and',\n conditions: expression.conditions.map(not),\n };\n case 'correlatedSubquery':\n return {\n type: 'correlatedSubquery',\n related: expression.related,\n op: negateOperator(expression.op),\n ...(expression.flip !== undefined ? {flip: expression.flip} : {}),\n ...(expression.scalar !== undefined ? {scalar: expression.scalar} : {}),\n };\n case 'simple':\n return {\n type: 'simple',\n op: negateOperator(expression.op),\n left: expression.left,\n right: expression.right,\n };\n }\n}\n\nexport function cmp(\n field: string,\n opOrValue: SimpleOperator | ParameterReference | LiteralValue | undefined,\n value?: ParameterReference | LiteralValue,\n): Condition {\n let op: SimpleOperator;\n let actualValue: ParameterReference | LiteralValue | undefined;\n\n if (arguments.length === 2) {\n // 2-arg form: cmp(field, value) - defaults to '=' operator\n actualValue = opOrValue as ParameterReference | LiteralValue | undefined;\n op = '=';\n } else {\n // 3-arg form: cmp(field, op, value)\n op = opOrValue as SimpleOperator;\n actualValue = value;\n }\n\n return {\n type: 'simple',\n left: {type: 'column', name: field},\n right: isParameterReference(actualValue)\n ? actualValue[toStaticParam]()\n : {type: 'literal', value: actualValue ?? null},\n op,\n };\n}\n\nfunction isParameterReference(\n value: ParameterReference | LiteralValue | null | undefined,\n): value is ParameterReference {\n return (\n value !== null &&\n value !== undefined &&\n typeof value === 'object' &&\n (value as any)[toStaticParam]\n );\n}\n\nexport const TRUE: Condition = {\n type: 'and',\n conditions: [],\n};\n\nconst FALSE: Condition = {\n type: 'or',\n conditions: [],\n};\n\nfunction isAlwaysTrue(condition: Condition): boolean {\n return condition.type === 'and' && condition.conditions.length === 0;\n}\n\nfunction isAlwaysFalse(condition: Condition): boolean {\n return condition.type === 'or' && condition.conditions.length === 0;\n}\n\nexport function simplifyCondition(c: Condition): Condition {\n if (c.type === 'simple' || c.type === 'correlatedSubquery') {\n return c;\n }\n if (c.conditions.length === 1) {\n return simplifyCondition(c.conditions[0]);\n }\n const conditions = flatten(c.type, c.conditions.map(simplifyCondition));\n if (c.type === 'and' && conditions.some(isAlwaysFalse)) {\n return FALSE;\n }\n if (c.type === 'or' && conditions.some(isAlwaysTrue)) {\n return TRUE;\n }\n return {\n type: c.type,\n conditions,\n };\n}\n\nexport function flatten(\n type: 'and' | 'or',\n conditions: readonly Condition[],\n): Condition[] {\n const flattened: Condition[] = [];\n for (const c of conditions) {\n if (c.type === type) {\n flattened.push(...c.conditions);\n } else {\n flattened.push(c);\n }\n }\n\n return flattened;\n}\n\nconst negateSimpleOperatorMap = {\n ['=']: '!=',\n ['!=']: '=',\n ['<']: '>=',\n ['>']: '<=',\n ['>=']: '<',\n ['<=']: '>',\n ['IN']: 'NOT IN',\n ['NOT IN']: 'IN',\n ['LIKE']: 'NOT LIKE',\n ['NOT LIKE']: 'LIKE',\n ['ILIKE']: 'NOT ILIKE',\n ['NOT ILIKE']: 'ILIKE',\n ['IS']: 'IS NOT',\n ['IS NOT']: 'IS',\n} as const;\n\nconst negateOperatorMap = {\n ...negateSimpleOperatorMap,\n ['EXISTS']: 'NOT EXISTS',\n ['NOT EXISTS']: 'EXISTS',\n} as const;\n\nexport function negateOperator<OP extends keyof typeof negateOperatorMap>(\n op: OP,\n): (typeof negateOperatorMap)[OP] {\n return must(negateOperatorMap[op]);\n}\n\nfunction filterUndefined<T>(array: (T | undefined)[]): T[] {\n return array.filter(e => e !== undefined);\n}\n\nfunction filterTrue(conditions: Condition[]): Condition[] {\n return conditions.filter(c => !isAlwaysTrue(c));\n}\n\nfunction filterFalse(conditions: Condition[]): Condition[] {\n return conditions.filter(c => !isAlwaysFalse(c));\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"measure-push-operator.js","names":[
|
|
1
|
+
{"version":3,"file":"measure-push-operator.js","names":[],"sources":["../../../../../zql/src/query/measure-push-operator.ts"],"sourcesContent":["import {emptyArray} from '../../../shared/src/sentinels.ts';\nimport type {Change} from '../ivm/change.ts';\nimport type {Node} from '../ivm/data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type InputBase,\n type Operator,\n type Output,\n} from '../ivm/operator.ts';\nimport type {SourceSchema} from '../ivm/schema.ts';\nimport type {Stream} from '../ivm/stream.ts';\nimport type {MetricsDelegate} from './metrics-delegate.ts';\n\ntype MetricName = 'query-update-client' | 'query-update-server';\n\nexport class MeasurePushOperator implements Operator {\n readonly #input: Input;\n readonly #queryID: string;\n readonly #metricsDelegate: MetricsDelegate;\n readonly #sampleRate: number;\n /** Pushes and reconciles so far, sampled or not. */\n #count = 0;\n\n #output: Output = throwOutput;\n readonly #metricName: MetricName;\n\n /**\n * With a `sampleRate` of n, only the first of every n pushes (and\n * reconciles) is timed and reported. The others go straight to the output,\n * without a timer or the generator that measuring needs. The metrics\n * delegate should weight each reported time by n.\n */\n constructor(\n input: Input,\n queryID: string,\n metricsDelegate: MetricsDelegate,\n metricName: MetricName,\n sampleRate = 1,\n ) {\n this.#input = input;\n this.#queryID = queryID;\n this.#metricsDelegate = metricsDelegate;\n this.#metricName = metricName;\n this.#sampleRate = sampleRate;\n input.setOutput(this);\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n fetch(req: FetchRequest): Stream<Node | 'yield'> {\n return this.#input.fetch(req);\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n push(change: Change): Stream<'yield'> {\n if (!this.#sample()) {\n return this.#output.push(change, this);\n }\n return this.#measure(() => this.#output.push(change, this));\n }\n\n reconcile(_pusher: InputBase): Stream<'yield'> {\n const output = this.#output;\n if (output.reconcile === undefined) {\n return emptyArray;\n }\n if (!this.#sample()) {\n return output.reconcile(this);\n }\n const reconcile = output.reconcile.bind(output);\n return this.#measure(() => reconcile(this));\n }\n\n #sample(): boolean {\n return this.#count++ % this.#sampleRate === 0;\n }\n\n /**\n * Runs the stream returned by `run` and records the time spent running it.\n *\n * The time the stream spends suspended at a `yield` is not counted: the\n * caller yields the thread there, and whatever else runs in the meantime\n * is not this query's work.\n *\n * The time is recorded even if the stream throws or is abandoned, so that\n * a push aborted for taking too long is accounted to its query.\n */\n *#measure(run: () => Stream<'yield'>): Stream<'yield'> {\n let elapsed = 0;\n let start = performance.now();\n let running = true;\n try {\n for (const result of run()) {\n elapsed += performance.now() - start;\n running = false;\n yield result;\n running = true;\n start = performance.now();\n }\n } finally {\n if (running) {\n elapsed += performance.now() - start;\n }\n this.#metricsDelegate.addMetric(this.#metricName, elapsed, this.#queryID);\n }\n }\n}\n"],"mappings":";;;AAiBA,IAAa,sBAAb,MAAqD;CACnD;CACA;CACA;CACA;;CAEA,SAAS;CAET,UAAkB;CAClB;;;;;;;CAQA,YACE,OACA,SACA,iBACA,YACA,aAAa,GACb;EACA,KAAK,SAAS;EACd,KAAK,WAAW;EAChB,KAAK,mBAAmB;EACxB,KAAK,cAAc;EACnB,KAAK,cAAc;EACnB,MAAM,UAAU,IAAI;CACtB;CAEA,UAAU,QAAsB;EAC9B,KAAK,UAAU;CACjB;CAEA,MAAM,KAA2C;EAC/C,OAAO,KAAK,OAAO,MAAM,GAAG;CAC9B;CAEA,YAA0B;EACxB,OAAO,KAAK,OAAO,UAAU;CAC/B;CAEA,UAAgB;EACd,KAAK,OAAO,QAAQ;CACtB;CAEA,KAAK,QAAiC;EACpC,IAAI,CAAC,KAAK,QAAQ,GAChB,OAAO,KAAK,QAAQ,KAAK,QAAQ,IAAI;EAEvC,OAAO,KAAK,eAAe,KAAK,QAAQ,KAAK,QAAQ,IAAI,CAAC;CAC5D;CAEA,UAAU,SAAqC;EAC7C,MAAM,SAAS,KAAK;EACpB,IAAI,OAAO,cAAc,KAAA,GACvB,OAAO;EAET,IAAI,CAAC,KAAK,QAAQ,GAChB,OAAO,OAAO,UAAU,IAAI;EAE9B,MAAM,YAAY,OAAO,UAAU,KAAK,MAAM;EAC9C,OAAO,KAAK,eAAe,UAAU,IAAI,CAAC;CAC5C;CAEA,UAAmB;EACjB,OAAO,KAAK,WAAW,KAAK,gBAAgB;CAC9C;;;;;;;;;;;CAYA,CAAC,SAAS,KAA6C;EACrD,IAAI,UAAU;EACd,IAAI,QAAQ,YAAY,IAAI;EAC5B,IAAI,UAAU;EACd,IAAI;GACF,KAAK,MAAM,UAAU,IAAI,GAAG;IAC1B,WAAW,YAAY,IAAI,IAAI;IAC/B,UAAU;IACV,MAAM;IACN,UAAU;IACV,QAAQ,YAAY,IAAI;GAC1B;EACF,UAAU;GACR,IAAI,SACF,WAAW,YAAY,IAAI,IAAI;GAEjC,KAAK,iBAAiB,UAAU,KAAK,aAAa,SAAS,KAAK,QAAQ;EAC1E;CACF;AACF"}
|
|
@@ -33,7 +33,8 @@ function syncedQueryWithContext(name, parser, fn) {
|
|
|
33
33
|
/** @deprecated */
|
|
34
34
|
function syncedQueryImpl(name, fn, takesContext) {
|
|
35
35
|
return (context, args) => {
|
|
36
|
-
|
|
36
|
+
const q = takesContext ? fn(context, ...args) : fn(...args);
|
|
37
|
+
return asQueryInternals(q).nameAndArgs(name, args);
|
|
37
38
|
};
|
|
38
39
|
}
|
|
39
40
|
/** @deprecated */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"named.js","names":[],"sources":["../../../../../zql/src/query/named.ts"],"sourcesContent":["import type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {QueryParseError} from './error.ts';\nimport {asQueryInternals} from './query-internals.ts';\nimport {type AnyQuery} from './query.ts';\n\n/** @deprecated */\nexport type QueryFn<\n TContext,\n TTakesContext extends boolean,\n TArg extends ReadonlyJSONValue[],\n TReturnQuery extends AnyQuery,\n> = TTakesContext extends false\n ? {(...args: TArg): TReturnQuery}\n : {(context: TContext, ...args: TArg): TReturnQuery};\n\n/** @deprecated */\nexport type SyncedQuery<\n TName extends string,\n TContext,\n TTakesContext extends boolean,\n TArg extends ReadonlyJSONValue[],\n TReturnQuery extends AnyQuery,\n> = QueryFn<TContext, TTakesContext, TArg, TReturnQuery> & {\n queryName: TName;\n parse: ParseFn<TArg> | undefined;\n takesContext: TTakesContext;\n};\n\nfunction normalizeParser<T extends ReadonlyJSONValue[]>(\n parser: ParseFn<T> | HasParseFn<T> | undefined,\n): ParseFn<T> | undefined {\n if (parser) {\n if ('parse' in parser) {\n return parser.parse.bind(parser);\n }\n return parser;\n }\n return undefined;\n}\n\n/**\n * @deprecated Use {@link defineQuery} instead.\n */\nexport function syncedQuery<\n TName extends string,\n TArg extends ReadonlyJSONValue[],\n TReturnQuery extends AnyQuery,\n>(\n name: TName,\n parser: ParseFn<TArg> | HasParseFn<TArg> | undefined,\n fn: QueryFn<unknown, false, TArg, TReturnQuery>,\n): SyncedQuery<TName, unknown, false, TArg, TReturnQuery> {\n const impl = syncedQueryImpl(name, fn, false);\n // oxlint-disable-next-line no-explicit-any\n const ret: any = (...args: TArg) => impl(undefined, args);\n ret.queryName = name;\n ret.parse = normalizeParser(parser);\n ret.takesContext = false;\n return ret;\n}\n\n/**\n * @deprecated Use {@link defineQuery} instead.\n */\nexport function syncedQueryWithContext<\n TName extends string,\n TContext,\n TArg extends ReadonlyJSONValue[],\n TReturnQuery extends AnyQuery,\n>(\n name: TName,\n parser: ParseFn<TArg> | HasParseFn<TArg> | undefined,\n fn: QueryFn<TContext, true, TArg, TReturnQuery>,\n): SyncedQuery<TName, TContext, true, TArg, TReturnQuery> {\n const impl = syncedQueryImpl(name, fn, true);\n // oxlint-disable-next-line no-explicit-any\n const ret: any = (context: TContext, ...args: TArg) => impl(context, args);\n ret.queryName = name;\n ret.parse = normalizeParser(parser);\n ret.takesContext = true;\n return ret;\n}\n\n/** @deprecated */\nfunction syncedQueryImpl<\n TName extends string,\n TContext,\n TArg extends ReadonlyJSONValue[],\n // oxlint-disable-next-line no-explicit-any\n>(name: TName, fn: any, takesContext: boolean) {\n return (context: TContext, args: TArg) => {\n const q = takesContext ? fn(context, ...args) : fn(...args);\n return asQueryInternals(q).nameAndArgs(name, args);\n };\n}\n\n/** @deprecated */\n\n// oxlint-disable-next-line no-explicit-any\ntype AnySyncedQuery = SyncedQuery<any, any, any, any, any>;\n\n/** @deprecated */\nexport function withValidation<F extends AnySyncedQuery>(\n fn: F,\n // oxlint-disable-next-line no-explicit-any\n): F extends SyncedQuery<infer N, infer C, any, any, infer R>\n ? SyncedQuery<N, C, true, ReadonlyJSONValue[], R>\n : F {\n // If we have a parse function this is a SyncedQuery\n if ('parse' in fn) {\n const {parse} = fn;\n if (!parse) {\n throw new Error('ret does not have a parse function defined');\n }\n // oxlint-disable-next-line no-explicit-any\n const ret: any = (context: unknown, ...args: unknown[]) => {\n let parsed;\n try {\n parsed = parse(args);\n } catch (error) {\n throw new QueryParseError({cause: error});\n }\n // oxlint-disable-next-line no-explicit-any\n return fn.takesContext ? fn(context, ...parsed) : (fn as any)(...parsed);\n };\n ret.queryName = fn.queryName;\n ret.parse = fn.parse;\n ret.takesContext = true;\n\n return ret;\n }\n\n // If we don't have a parse function, return the function as-is\n // (this shouldn't happen in practice)\n // oxlint-disable-next-line no-explicit-any\n return fn as any;\n}\n\n/** @deprecated */\nexport type ParseFn<T extends ReadonlyJSONValue[]> = (args: unknown[]) => T;\n\n/** @deprecated */\nexport type HasParseFn<T extends ReadonlyJSONValue[]> = {\n parse: ParseFn<T>;\n};\n\n/** @deprecated */\nexport type Parser<T extends ReadonlyJSONValue[]> = ParseFn<T> | HasParseFn<T>;\n\nexport type CustomQueryID = {\n name: string;\n args: ReadonlyArray<ReadonlyJSONValue>;\n};\n"],"mappings":";;;;AA4BA,SAAS,gBACP,QACwB;CACxB,IAAI,QAAQ;EACV,IAAI,WAAW,QACb,OAAO,OAAO,MAAM,KAAK,MAAM;EAEjC,OAAO;CACT;AAEF;;;;AAKA,SAAgB,YAKd,MACA,QACA,IACwD;CACxD,MAAM,OAAO,gBAAgB,MAAM,IAAI,KAAK;CAE5C,MAAM,OAAY,GAAG,SAAe,KAAK,KAAA,GAAW,IAAI;CACxD,IAAI,YAAY;CAChB,IAAI,QAAQ,gBAAgB,MAAM;CAClC,IAAI,eAAe;CACnB,OAAO;AACT;;;;AAKA,SAAgB,uBAMd,MACA,QACA,IACwD;CACxD,MAAM,OAAO,gBAAgB,MAAM,IAAI,IAAI;CAE3C,MAAM,OAAY,SAAmB,GAAG,SAAe,KAAK,SAAS,IAAI;CACzE,IAAI,YAAY;CAChB,IAAI,QAAQ,gBAAgB,MAAM;CAClC,IAAI,eAAe;CACnB,OAAO;AACT;;AAGA,SAAS,gBAKP,MAAa,IAAS,cAAuB;CAC7C,QAAQ,SAAmB,SAAe;
|
|
1
|
+
{"version":3,"file":"named.js","names":[],"sources":["../../../../../zql/src/query/named.ts"],"sourcesContent":["import type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {QueryParseError} from './error.ts';\nimport {asQueryInternals} from './query-internals.ts';\nimport {type AnyQuery} from './query.ts';\n\n/** @deprecated */\nexport type QueryFn<\n TContext,\n TTakesContext extends boolean,\n TArg extends ReadonlyJSONValue[],\n TReturnQuery extends AnyQuery,\n> = TTakesContext extends false\n ? {(...args: TArg): TReturnQuery}\n : {(context: TContext, ...args: TArg): TReturnQuery};\n\n/** @deprecated */\nexport type SyncedQuery<\n TName extends string,\n TContext,\n TTakesContext extends boolean,\n TArg extends ReadonlyJSONValue[],\n TReturnQuery extends AnyQuery,\n> = QueryFn<TContext, TTakesContext, TArg, TReturnQuery> & {\n queryName: TName;\n parse: ParseFn<TArg> | undefined;\n takesContext: TTakesContext;\n};\n\nfunction normalizeParser<T extends ReadonlyJSONValue[]>(\n parser: ParseFn<T> | HasParseFn<T> | undefined,\n): ParseFn<T> | undefined {\n if (parser) {\n if ('parse' in parser) {\n return parser.parse.bind(parser);\n }\n return parser;\n }\n return undefined;\n}\n\n/**\n * @deprecated Use {@link defineQuery} instead.\n */\nexport function syncedQuery<\n TName extends string,\n TArg extends ReadonlyJSONValue[],\n TReturnQuery extends AnyQuery,\n>(\n name: TName,\n parser: ParseFn<TArg> | HasParseFn<TArg> | undefined,\n fn: QueryFn<unknown, false, TArg, TReturnQuery>,\n): SyncedQuery<TName, unknown, false, TArg, TReturnQuery> {\n const impl = syncedQueryImpl(name, fn, false);\n // oxlint-disable-next-line no-explicit-any\n const ret: any = (...args: TArg) => impl(undefined, args);\n ret.queryName = name;\n ret.parse = normalizeParser(parser);\n ret.takesContext = false;\n return ret;\n}\n\n/**\n * @deprecated Use {@link defineQuery} instead.\n */\nexport function syncedQueryWithContext<\n TName extends string,\n TContext,\n TArg extends ReadonlyJSONValue[],\n TReturnQuery extends AnyQuery,\n>(\n name: TName,\n parser: ParseFn<TArg> | HasParseFn<TArg> | undefined,\n fn: QueryFn<TContext, true, TArg, TReturnQuery>,\n): SyncedQuery<TName, TContext, true, TArg, TReturnQuery> {\n const impl = syncedQueryImpl(name, fn, true);\n // oxlint-disable-next-line no-explicit-any\n const ret: any = (context: TContext, ...args: TArg) => impl(context, args);\n ret.queryName = name;\n ret.parse = normalizeParser(parser);\n ret.takesContext = true;\n return ret;\n}\n\n/** @deprecated */\nfunction syncedQueryImpl<\n TName extends string,\n TContext,\n TArg extends ReadonlyJSONValue[],\n // oxlint-disable-next-line no-explicit-any\n>(name: TName, fn: any, takesContext: boolean) {\n return (context: TContext, args: TArg) => {\n const q = takesContext ? fn(context, ...args) : fn(...args);\n return asQueryInternals(q).nameAndArgs(name, args);\n };\n}\n\n/** @deprecated */\n\n// oxlint-disable-next-line no-explicit-any\ntype AnySyncedQuery = SyncedQuery<any, any, any, any, any>;\n\n/** @deprecated */\nexport function withValidation<F extends AnySyncedQuery>(\n fn: F,\n // oxlint-disable-next-line no-explicit-any\n): F extends SyncedQuery<infer N, infer C, any, any, infer R>\n ? SyncedQuery<N, C, true, ReadonlyJSONValue[], R>\n : F {\n // If we have a parse function this is a SyncedQuery\n if ('parse' in fn) {\n const {parse} = fn;\n if (!parse) {\n throw new Error('ret does not have a parse function defined');\n }\n // oxlint-disable-next-line no-explicit-any\n const ret: any = (context: unknown, ...args: unknown[]) => {\n let parsed;\n try {\n parsed = parse(args);\n } catch (error) {\n throw new QueryParseError({cause: error});\n }\n // oxlint-disable-next-line no-explicit-any\n return fn.takesContext ? fn(context, ...parsed) : (fn as any)(...parsed);\n };\n ret.queryName = fn.queryName;\n ret.parse = fn.parse;\n ret.takesContext = true;\n\n return ret;\n }\n\n // If we don't have a parse function, return the function as-is\n // (this shouldn't happen in practice)\n // oxlint-disable-next-line no-explicit-any\n return fn as any;\n}\n\n/** @deprecated */\nexport type ParseFn<T extends ReadonlyJSONValue[]> = (args: unknown[]) => T;\n\n/** @deprecated */\nexport type HasParseFn<T extends ReadonlyJSONValue[]> = {\n parse: ParseFn<T>;\n};\n\n/** @deprecated */\nexport type Parser<T extends ReadonlyJSONValue[]> = ParseFn<T> | HasParseFn<T>;\n\nexport type CustomQueryID = {\n name: string;\n args: ReadonlyArray<ReadonlyJSONValue>;\n};\n"],"mappings":";;;;AA4BA,SAAS,gBACP,QACwB;CACxB,IAAI,QAAQ;EACV,IAAI,WAAW,QACb,OAAO,OAAO,MAAM,KAAK,MAAM;EAEjC,OAAO;CACT;AAEF;;;;AAKA,SAAgB,YAKd,MACA,QACA,IACwD;CACxD,MAAM,OAAO,gBAAgB,MAAM,IAAI,KAAK;CAE5C,MAAM,OAAY,GAAG,SAAe,KAAK,KAAA,GAAW,IAAI;CACxD,IAAI,YAAY;CAChB,IAAI,QAAQ,gBAAgB,MAAM;CAClC,IAAI,eAAe;CACnB,OAAO;AACT;;;;AAKA,SAAgB,uBAMd,MACA,QACA,IACwD;CACxD,MAAM,OAAO,gBAAgB,MAAM,IAAI,IAAI;CAE3C,MAAM,OAAY,SAAmB,GAAG,SAAe,KAAK,SAAS,IAAI;CACzE,IAAI,YAAY;CAChB,IAAI,QAAQ,gBAAgB,MAAM;CAClC,IAAI,eAAe;CACnB,OAAO;AACT;;AAGA,SAAS,gBAKP,MAAa,IAAS,cAAuB;CAC7C,QAAQ,SAAmB,SAAe;EACxC,MAAM,IAAI,eAAe,GAAG,SAAS,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI;EAC1D,OAAO,iBAAiB,CAAC,CAAC,CAAC,YAAY,MAAM,IAAI;CACnD;AACF;;AAQA,SAAgB,eACd,IAII;CAEJ,IAAI,WAAW,IAAI;EACjB,MAAM,EAAC,UAAS;EAChB,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,4CAA4C;EAG9D,MAAM,OAAY,SAAkB,GAAG,SAAoB;GACzD,IAAI;GACJ,IAAI;IACF,SAAS,MAAM,IAAI;GACrB,SAAS,OAAO;IACd,MAAM,IAAI,gBAAgB,EAAC,OAAO,MAAK,CAAC;GAC1C;GAEA,OAAO,GAAG,eAAe,GAAG,SAAS,GAAG,MAAM,IAAK,GAAW,GAAG,MAAM;EACzE;EACA,IAAI,YAAY,GAAG;EACnB,IAAI,QAAQ,GAAG;EACf,IAAI,eAAe;EAEnB,OAAO;CACT;CAKA,OAAO;AACT"}
|
|
@@ -295,6 +295,6 @@ function arrayViewFactory(_query, input, format, onDestroy, onTransactionCommit,
|
|
|
295
295
|
return v;
|
|
296
296
|
}
|
|
297
297
|
//#endregion
|
|
298
|
-
export { QueryDelegateBase };
|
|
298
|
+
export { QueryDelegateBase, materializeImpl, preloadImpl, runImpl };
|
|
299
299
|
|
|
300
300
|
//# sourceMappingURL=query-delegate-base.js.map
|