@rocicorp/zero 1.6.0-canary.12 → 1.6.0-canary.13
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/README.md +3 -28
- package/out/_virtual/{_@oxc-project_runtime@0.130.0 → _@oxc-project_runtime@0.122.0}/helpers/usingCtx.js +1 -1
- package/out/analyze-query/src/analyze-cli.js +3 -3
- package/out/analyze-query/src/analyze-cli.js.map +1 -1
- package/out/analyze-query/src/bin-analyze.js +1 -6
- package/out/analyze-query/src/bin-analyze.js.map +1 -1
- 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.map +1 -1
- package/out/ast-to-zql/src/format.js.map +1 -1
- package/out/datadog/src/datadog-log-sink.js.map +1 -1
- package/out/otel/src/enabled.js.map +1 -1
- package/out/otel/src/log-options.js.map +1 -1
- package/out/otel/src/maybe-time.js.map +1 -1
- package/out/otel/src/span.js.map +1 -1
- package/out/replicache/src/async-iterable-to-array.js.map +1 -1
- package/out/replicache/src/bg-interval.js.map +1 -1
- package/out/replicache/src/btree/diff.js.map +1 -1
- package/out/replicache/src/btree/node.js.map +1 -1
- 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 +3 -6
- package/out/replicache/src/btree/write.js.map +1 -1
- package/out/replicache/src/call-default-fetch.js.map +1 -1
- package/out/replicache/src/connection-loop-delegates.js.map +1 -1
- package/out/replicache/src/connection-loop.js.map +1 -1
- package/out/replicache/src/cookies.js.map +1 -1
- package/out/replicache/src/dag/chunk.js.map +1 -1
- package/out/replicache/src/dag/gc.js.map +1 -1
- package/out/replicache/src/dag/key.js.map +1 -1
- package/out/replicache/src/dag/lazy-store.js.map +1 -1
- package/out/replicache/src/dag/store-impl.js.map +1 -1
- package/out/replicache/src/dag/store.js.map +1 -1
- package/out/replicache/src/dag/visitor.js.map +1 -1
- package/out/replicache/src/db/commit.js.map +1 -1
- package/out/replicache/src/db/index.js.map +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.map +1 -1
- package/out/replicache/src/deleted-clients.js.map +1 -1
- package/out/replicache/src/error-responses.js.map +1 -1
- package/out/replicache/src/frozen-json.js.map +1 -1
- package/out/replicache/src/get-default-puller.js.map +1 -1
- package/out/replicache/src/get-default-pusher.js.map +1 -1
- package/out/replicache/src/get-kv-store-provider.js.map +1 -1
- package/out/replicache/src/hash.js.map +1 -1
- package/out/replicache/src/http-request-info.js.map +1 -1
- package/out/replicache/src/index-defs.js.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +1 -1
- package/out/replicache/src/kv/idb-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/read-impl.js.map +1 -1
- package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
- package/out/replicache/src/kv/sqlite-store.js +1 -4
- package/out/replicache/src/kv/sqlite-store.js.map +1 -1
- package/out/replicache/src/kv/throw-if-closed.js.map +1 -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/lazy.js.map +1 -1
- package/out/replicache/src/log-options.js.map +1 -1
- package/out/replicache/src/make-idb-name.js.map +1 -1
- package/out/replicache/src/new-client-channel.js.map +1 -1
- package/out/replicache/src/on-persist-channel.js.map +1 -1
- package/out/replicache/src/patch-operation.js.map +1 -1
- package/out/replicache/src/pending-mutations.js.map +1 -1
- package/out/replicache/src/persist/client-gc.js.map +1 -1
- package/out/replicache/src/persist/client-group-gc.js.map +1 -1
- package/out/replicache/src/persist/client-groups.js +0 -40
- package/out/replicache/src/persist/client-groups.js.map +1 -1
- package/out/replicache/src/persist/clients.js +0 -28
- package/out/replicache/src/persist/clients.js.map +1 -1
- 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.map +1 -1
- package/out/replicache/src/persist/idb-databases-store-db-name.js.map +1 -1
- package/out/replicache/src/persist/idb-databases-store.js.map +1 -1
- package/out/replicache/src/persist/make-client-id.js.map +1 -1
- package/out/replicache/src/persist/persist.js.map +1 -1
- package/out/replicache/src/persist/refresh.js.map +1 -1
- package/out/replicache/src/process-scheduler.js.map +1 -1
- package/out/replicache/src/pusher.js.map +1 -1
- package/out/replicache/src/replicache-impl.js.map +1 -1
- package/out/replicache/src/report-error.js.map +1 -1
- package/out/replicache/src/request-idle.js.map +1 -1
- package/out/replicache/src/scan-iterator.js.map +1 -1
- package/out/replicache/src/scan-options.js.map +1 -1
- package/out/replicache/src/set-interval-with-signal.js.map +1 -1
- package/out/replicache/src/subscriptions.js.map +1 -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.map +1 -1
- package/out/replicache/src/sync/pull-error.js.map +1 -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.map +1 -1
- package/out/replicache/src/to-error.js.map +1 -1
- package/out/replicache/src/transaction-closed-error.js.map +1 -1
- package/out/replicache/src/transactions.js.map +1 -1
- package/out/replicache/src/with-transactions.js.map +1 -1
- package/out/shared/src/abort-error.js.map +1 -1
- package/out/shared/src/arrays.js.map +1 -1
- package/out/shared/src/asserts.js.map +1 -1
- package/out/shared/src/bigint-json.js.map +1 -1
- package/out/shared/src/binary-search.js.map +1 -1
- package/out/shared/src/broadcast-channel.js.map +1 -1
- package/out/shared/src/browser-env.js.map +1 -1
- package/out/shared/src/btree-set.js.map +1 -1
- package/out/shared/src/cache.js.map +1 -1
- package/out/shared/src/centroid.js.map +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.map +1 -1
- package/out/shared/src/deep-merge.js.map +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.map +1 -1
- package/out/shared/src/hash.js.map +1 -1
- package/out/shared/src/iterables.d.ts +0 -2
- package/out/shared/src/iterables.d.ts.map +1 -1
- package/out/shared/src/iterables.js +1 -9
- 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.map +1 -1
- package/out/shared/src/logging.js.map +1 -1
- package/out/shared/src/map.js.map +1 -1
- package/out/shared/src/must.js.map +1 -1
- package/out/shared/src/object-traversal.js.map +1 -1
- package/out/shared/src/objects.js.map +1 -1
- package/out/shared/src/options.js.map +1 -1
- package/out/shared/src/parse-big-int.js.map +1 -1
- package/out/shared/src/promise-race.js.map +1 -1
- package/out/shared/src/queue.d.ts.map +1 -1
- package/out/shared/src/queue.js +21 -15
- package/out/shared/src/queue.js.map +1 -1
- package/out/shared/src/rand.js.map +1 -1
- package/out/shared/src/random-uint64.js.map +1 -1
- package/out/shared/src/random-values.js.map +1 -1
- package/out/shared/src/record-proxy.js.map +1 -1
- package/out/shared/src/resolved-promises.js.map +1 -1
- package/out/shared/src/sentinels.js.map +1 -1
- package/out/shared/src/set-utils.js.map +1 -1
- package/out/shared/src/size-of-value.js.map +1 -1
- package/out/shared/src/sleep.js.map +1 -1
- package/out/shared/src/sorted-entries.js.map +1 -1
- package/out/shared/src/string-compare.js.map +1 -1
- package/out/shared/src/subscribable.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.map +1 -1
- package/out/z2s/src/compiler.js.map +1 -1
- package/out/z2s/src/sql.js.map +1 -1
- package/out/zero/package.js +26 -34
- package/out/zero/package.js.map +1 -1
- package/out/zero/src/build-schema.js.map +1 -1
- package/out/zero/src/zero-cache-dev.js.map +1 -1
- package/out/zero/src/zero-out.js.map +1 -1
- package/out/zero-cache/src/auth/auth.js.map +1 -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.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
- package/out/zero-cache/src/config/network.js.map +1 -1
- package/out/zero-cache/src/config/normalize.js.map +1 -1
- package/out/zero-cache/src/config/server-context.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +0 -5
- package/out/zero-cache/src/config/zero-config.js.map +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/delete-lite-db.js.map +1 -1
- package/out/zero-cache/src/db/lite-tables.js.map +1 -1
- package/out/zero-cache/src/db/migration-lite.js +0 -19
- package/out/zero-cache/src/db/migration-lite.js.map +1 -1
- package/out/zero-cache/src/db/migration.js +0 -19
- package/out/zero-cache/src/db/migration.js.map +1 -1
- package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -1
- package/out/zero-cache/src/db/pg-copy.js.map +1 -1
- package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
- package/out/zero-cache/src/db/pg-type-parser.js.map +1 -1
- package/out/zero-cache/src/db/run-transaction.js.map +1 -1
- package/out/zero-cache/src/db/specs.js.map +1 -1
- package/out/zero-cache/src/db/statements.js.map +1 -1
- 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/metrics.js.map +1 -1
- package/out/zero-cache/src/scripts/decommission.js.map +1 -1
- package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
- package/out/zero-cache/src/scripts/permissions.d.ts.map +1 -1
- package/out/zero-cache/src/scripts/permissions.js +2 -1
- package/out/zero-cache/src/scripts/permissions.js.map +1 -1
- package/out/zero-cache/src/server/anonymous-otel-start.js +7 -8
- 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.map +1 -1
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/mutator.js.map +1 -1
- package/out/zero-cache/src/server/otel-diag-logger.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/priority-op.js.map +1 -1
- 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/main.js.map +1 -1
- package/out/zero-cache/src/server/runner/run-worker.js.map +1 -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.map +1 -1
- package/out/zero-cache/src/server/syncer.js.map +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.d.ts.map +1 -1
- package/out/zero-cache/src/services/analyze.js +2 -5
- package/out/zero-cache/src/services/analyze.js.map +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/replica-schema.js.map +1 -1
- 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.map +1 -1
- 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.map +1 -1
- 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.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-slots.js.map +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/init.js.map +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/shard.js.map +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/current/control.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/data.js +0 -2
- 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/json.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-streamer/backup-monitor.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
- 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.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/replica-monitor.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/init.js +25 -21
- 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.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot.js +0 -15
- package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -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.map +1 -1
- package/out/zero-cache/src/services/limiter/sliding-window-limiter.js.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/error.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js.map +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.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.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/report-schema.js.map +1 -1
- 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.map +1 -1
- 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/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.d.ts.map +1 -1
- package/out/zero-cache/src/services/run-ast.js +0 -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.map +1 -1
- package/out/zero-cache/src/services/view-syncer/active-users-gauge.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
- 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.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-purger.js +1 -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 +1 -2
- package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.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/inspect-handler.d.ts +14 -0
- package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js +25 -2
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.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/row-set-signature.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/init.js +113 -97
- package/out/zero-cache/src/services/view-syncer/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/types.js +1 -103
- package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/tracer.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.js +1 -4
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/configuration-error.js.map +1 -1
- package/out/zero-cache/src/types/error-with-level.js.map +1 -1
- package/out/zero-cache/src/types/http.js.map +1 -1
- package/out/zero-cache/src/types/lexi-version.js.map +1 -1
- package/out/zero-cache/src/types/lite.js.map +1 -1
- package/out/zero-cache/src/types/names.js.map +1 -1
- package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
- package/out/zero-cache/src/types/pg.js.map +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/shards.js.map +1 -1
- package/out/zero-cache/src/types/sql.js.map +1 -1
- package/out/zero-cache/src/types/state-version.js.map +1 -1
- 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.map +1 -1
- package/out/zero-cache/src/types/url-params.js.map +1 -1
- package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
- package/out/zero-cache/src/types/ws.js.map +1 -1
- package/out/zero-cache/src/workers/connect-params.js.map +1 -1
- 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.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
- package/out/zero-client/src/client/connection-manager.js +1 -2
- package/out/zero-client/src/client/connection-manager.js.map +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.map +1 -1
- package/out/zero-client/src/client/crud.js.map +1 -1
- package/out/zero-client/src/client/custom.js +1 -2
- 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.map +1 -1
- package/out/zero-client/src/client/error.js.map +1 -1
- package/out/zero-client/src/client/http-string.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.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.map +1 -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-mutate-property.js.map +1 -1
- package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
- 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.map +1 -1
- package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
- package/out/zero-client/src/client/server-option.js.map +1 -1
- package/out/zero-client/src/client/version.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.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +32 -58
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-client/src/util/nanoid.js.map +1 -1
- package/out/zero-client/src/util/socket.d.ts +3 -0
- package/out/zero-client/src/util/socket.d.ts.map +1 -0
- package/out/zero-client/src/util/socket.js +8 -0
- package/out/zero-client/src/util/socket.js.map +1 -0
- package/out/zero-protocol/src/analyze-query-result.js +0 -3
- 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.map +1 -1
- package/out/zero-protocol/src/change-desired-queries.js +0 -1
- package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
- package/out/zero-protocol/src/client-schema.js.map +1 -1
- package/out/zero-protocol/src/close-connection.js.map +1 -1
- package/out/zero-protocol/src/connect.js +0 -7
- package/out/zero-protocol/src/connect.js.map +1 -1
- package/out/zero-protocol/src/custom-queries.js.map +1 -1
- 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.js.map +1 -1
- package/out/zero-protocol/src/error.js +0 -7
- package/out/zero-protocol/src/error.js.map +1 -1
- package/out/zero-protocol/src/inspect-down.js.map +1 -1
- package/out/zero-protocol/src/inspect-up.js +0 -1
- package/out/zero-protocol/src/inspect-up.js.map +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.map +1 -1
- package/out/zero-protocol/src/mutations-patch.js.map +1 -1
- package/out/zero-protocol/src/ping.js.map +1 -1
- package/out/zero-protocol/src/poke.js +0 -4
- package/out/zero-protocol/src/poke.js.map +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.map +1 -1
- package/out/zero-protocol/src/pull.js.map +1 -1
- package/out/zero-protocol/src/push.js +0 -16
- package/out/zero-protocol/src/push.js.map +1 -1
- package/out/zero-protocol/src/queries-patch.js.map +1 -1
- package/out/zero-protocol/src/query-hash.js.map +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.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/use-connection-state.js.map +1 -1
- package/out/zero-react/src/use-query.js.map +1 -1
- package/out/zero-react/src/use-zero-online.js.map +1 -1
- package/out/zero-react/src/zero-provider.js.map +1 -1
- package/out/zero-schema/src/builder/relationship-builder.js.map +1 -1
- package/out/zero-schema/src/builder/schema-builder.js.map +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.map +1 -1
- package/out/zero-schema/src/permissions.js.map +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/custom.js +1 -2
- package/out/zero-server/src/custom.js.map +1 -1
- package/out/zero-server/src/logging.js.map +1 -1
- package/out/zero-server/src/pg-query-executor.js.map +1 -1
- 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.map +1 -1
- 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/solid-view.js.map +1 -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.map +1 -1
- package/out/zero-solid/src/use-zero.js.map +1 -1
- package/out/zero-types/src/format.js.map +1 -1
- package/out/zero-types/src/name-mapper.js.map +1 -1
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/builder/debug-delegate.d.ts +0 -5
- package/out/zql/src/builder/debug-delegate.d.ts.map +1 -1
- package/out/zql/src/builder/debug-delegate.js +1 -10
- package/out/zql/src/builder/debug-delegate.js.map +1 -1
- package/out/zql/src/builder/filter.js.map +1 -1
- package/out/zql/src/builder/like.js.map +1 -1
- package/out/zql/src/error.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/change.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/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.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.d.ts +8 -4
- package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
- package/out/zql/src/ivm/flipped-join.js +63 -59
- package/out/zql/src/ivm/flipped-join.js.map +1 -1
- 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.map +1 -1
- package/out/zql/src/ivm/memory-storage.js.map +1 -1
- package/out/zql/src/ivm/operator.d.ts +1 -1
- package/out/zql/src/ivm/operator.js.map +1 -1
- package/out/zql/src/ivm/push-accumulated.js.map +1 -1
- package/out/zql/src/ivm/schema.d.ts +8 -0
- package/out/zql/src/ivm/schema.d.ts.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/source.js.map +1 -1
- package/out/zql/src/ivm/stream.js.map +1 -1
- package/out/zql/src/ivm/take.js.map +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.map +1 -1
- package/out/zql/src/mutate/crud.js.map +1 -1
- package/out/zql/src/mutate/custom.js.map +1 -1
- package/out/zql/src/mutate/mutator-registry.js.map +1 -1
- package/out/zql/src/mutate/mutator.js.map +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-constraint.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-graph.js.map +1 -1
- package/out/zql/src/planner/planner-join.d.ts.map +1 -1
- package/out/zql/src/planner/planner-join.js +1 -2
- package/out/zql/src/planner/planner-join.js.map +1 -1
- package/out/zql/src/planner/planner-node.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/complete-ordering.js.map +1 -1
- package/out/zql/src/query/create-builder.js.map +1 -1
- package/out/zql/src/query/error.js.map +1 -1
- package/out/zql/src/query/escape-like.js.map +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/metrics-delegate.js.map +1 -1
- package/out/zql/src/query/named.js.map +1 -1
- package/out/zql/src/query/query-delegate-base.js.map +1 -1
- package/out/zql/src/query/query-impl.js +1 -1
- package/out/zql/src/query/query-impl.js.map +1 -1
- package/out/zql/src/query/query-internals.js.map +1 -1
- package/out/zql/src/query/query-registry.js.map +1 -1
- 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.map +1 -1
- package/out/zql/src/query/validate-input.js.map +1 -1
- package/out/zqlite/src/database-storage.js.map +1 -1
- package/out/zqlite/src/db.js.map +1 -1
- package/out/zqlite/src/explain-queries.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.js.map +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.map +1 -1
- 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.map +1 -1
- package/out/zqlite/src/table-source.js +6 -6
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +26 -42
- package/out/shared/src/ring-buffer.d.ts +0 -32
- package/out/shared/src/ring-buffer.d.ts.map +0 -1
- package/out/shared/src/ring-buffer.js +0 -109
- package/out/shared/src/ring-buffer.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exists.js","names":["#input","#relationshipName","#not","#parentJoinKey","#noSizeReuse","#cache","#cacheHitCountsForTesting","#output","#inPush","#getCacheKey","#fetchExists","#filter","#pushWithFilter","#fetchSize"],"sources":["../../../../../zql/src/ivm/exists.ts"],"sourcesContent":["import {areEqual} from '../../../shared/src/arrays.ts';\nimport {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport type {CompoundKey} from '../../../zero-protocol/src/ast.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {makeAddChange, makeRemoveChange, type Change} from './change.ts';\nimport {normalizeUndefined, type Node, type NormalizedValue} from './data.ts';\nimport {\n throwFilterOutput,\n type FilterInput,\n type FilterOperator,\n type FilterOutput,\n} from './filter-operators.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\n/**\n * The Exists operator filters data based on whether or not a relationship is\n * non-empty.\n */\nexport class Exists implements FilterOperator {\n readonly #input: FilterInput;\n readonly #relationshipName: string;\n readonly #not: boolean;\n readonly #parentJoinKey: CompoundKey;\n readonly #noSizeReuse: boolean;\n #cache: Map<string, boolean>;\n #cacheHitCountsForTesting: Map<string, number> | undefined;\n #output: FilterOutput = throwFilterOutput;\n\n /**\n * This instance variable is `true` when this operator is processing a `push`,\n * and is used to disable reuse of cached sizes across rows with the\n * same parent join key value.\n * This is necessary because during a push relationships can be inconsistent\n * due to push communicating changes (which may change multiple Nodes) one\n * Node at a time.\n */\n #inPush = false;\n\n constructor(\n input: FilterInput,\n relationshipName: string,\n parentJoinKey: CompoundKey,\n type: 'EXISTS' | 'NOT EXISTS',\n cacheHitCountsForTesting?: Map<string, number>,\n ) {\n this.#input = input;\n this.#relationshipName = relationshipName;\n this.#input.setFilterOutput(this);\n this.#cache = new Map();\n this.#cacheHitCountsForTesting = cacheHitCountsForTesting;\n assert(\n this.#input.getSchema().relationships[relationshipName],\n `Input schema missing ${relationshipName}`,\n );\n this.#not = type === 'NOT EXISTS';\n this.#parentJoinKey = parentJoinKey;\n\n // If the parentJoinKey is the primary key, no sense in trying to reuse.\n this.#noSizeReuse = areEqual(\n parentJoinKey,\n this.#input.getSchema().primaryKey,\n );\n }\n\n setFilterOutput(output: FilterOutput): void {\n this.#output = output;\n }\n\n beginFilter() {\n this.#output.beginFilter();\n }\n\n endFilter() {\n this.#cache = new Map();\n this.#output.endFilter();\n }\n\n *filter(node: Node): Generator<'yield', boolean> {\n let exists: boolean | undefined;\n if (!this.#noSizeReuse && !this.#inPush) {\n const key = this.#getCacheKey(node, this.#parentJoinKey);\n exists = this.#cache.get(key);\n if (exists === undefined) {\n exists = yield* this.#fetchExists(node);\n this.#cache.set(key, exists);\n } else if (this.#cacheHitCountsForTesting) {\n this.#cacheHitCountsForTesting.set(\n key,\n (this.#cacheHitCountsForTesting.get(key) ?? 0) + 1,\n );\n }\n }\n\n const result =\n (yield* this.#filter(node, exists)) && (yield* this.#output.filter(node));\n return result;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n *push(change: Change): Stream<'yield'> {\n assert(!this.#inPush, 'Unexpected re-entrancy');\n this.#inPush = true;\n try {\n switch (change[ChangeIndex.TYPE]) {\n // add, remove and edit cannot change the size of the\n // this.#relationshipName relationship, so simply #pushWithFilter\n case ChangeType.ADD:\n case ChangeType.EDIT:\n case ChangeType.REMOVE: {\n yield* this.#pushWithFilter(change);\n return;\n }\n case ChangeType.CHILD:\n // Only add and remove child changes for the\n // this.#relationshipName relationship, can change the size\n // of the this.#relationshipName relationship, for other\n // child changes simply #pushWithFilter\n if (\n change[ChangeIndex.CHILD_DATA].relationshipName !==\n this.#relationshipName ||\n change[ChangeIndex.CHILD_DATA].change[ChangeIndex.TYPE] ===\n ChangeType.EDIT ||\n change[ChangeIndex.CHILD_DATA].change[ChangeIndex.TYPE] ===\n ChangeType.CHILD\n ) {\n yield* this.#pushWithFilter(change);\n return;\n }\n switch (change[ChangeIndex.CHILD_DATA].change[ChangeIndex.TYPE]) {\n case ChangeType.ADD: {\n const size = yield* this.#fetchSize(change[ChangeIndex.NODE]);\n if (size === 1) {\n if (this.#not) {\n // Since the add child change currently being processed is not\n // pushed to output, the added child needs to be excluded from\n // the remove being pushed to output (since the child has\n // never been added to the output).\n yield* this.#output.push(\n makeRemoveChange({\n row: change[ChangeIndex.NODE].row,\n relationships: {\n ...change[ChangeIndex.NODE].relationships,\n [this.#relationshipName]: () => [],\n },\n }),\n this,\n );\n } else {\n yield* this.#output.push(\n makeAddChange(change[ChangeIndex.NODE]),\n this,\n );\n }\n } else {\n yield* this.#pushWithFilter(change, size > 0);\n }\n return;\n }\n case ChangeType.REMOVE: {\n const size = yield* this.#fetchSize(change[ChangeIndex.NODE]);\n if (size === 0) {\n if (this.#not) {\n yield* this.#output.push(\n makeAddChange(change[ChangeIndex.NODE]),\n this,\n );\n } else {\n // Since the remove child change currently being processed is\n // not pushed to output, the removed child needs to be added to\n // the remove being pushed to output.\n yield* this.#output.push(\n makeRemoveChange({\n row: change[ChangeIndex.NODE].row,\n relationships: {\n ...change[ChangeIndex.NODE].relationships,\n [this.#relationshipName]: () => [\n change[ChangeIndex.CHILD_DATA].change[\n ChangeIndex.NODE\n ],\n ],\n },\n }),\n this,\n );\n }\n } else {\n yield* this.#pushWithFilter(change, size > 0);\n }\n return;\n }\n }\n return;\n default:\n unreachable(change);\n }\n } finally {\n this.#inPush = false;\n }\n }\n\n /**\n * Returns whether or not the node's this.#relationshipName\n * relationship passes the exist/not exists filter condition.\n * If the optional `size` is passed it is used.\n * Otherwise, if there is a stored size for the row it is used.\n * Otherwise the size is computed by streaming the node's\n * relationship with this.#relationshipName (this computed size is also\n * stored).\n */\n *#filter(node: Node, exists?: boolean): Generator<'yield', boolean> {\n exists = exists ?? (yield* this.#fetchExists(node));\n return this.#not ? !exists : exists;\n }\n\n #getCacheKey(node: Node, def: CompoundKey): string {\n const values: NormalizedValue[] = [];\n for (const key of def) {\n values.push(normalizeUndefined(node.row[key]));\n }\n return JSON.stringify(values);\n }\n\n /**\n * Pushes a change if this.#filter is true for its row.\n */\n *#pushWithFilter(change: Change, exists?: boolean): Stream<'yield'> {\n if (yield* this.#filter(change[ChangeIndex.NODE], exists)) {\n yield* this.#output.push(change, this);\n }\n }\n\n *#fetchExists(node: Node): Generator<'yield', boolean> {\n // While it seems like this should be able to fetch just 1 node\n // to check for exists, we can't because Take does not support\n // early return during initial fetch.\n return (yield* this.#fetchSize(node)) > 0;\n }\n\n *#fetchSize(node: Node): Generator<'yield', number> {\n const relationship = node.relationships[this.#relationshipName];\n assert(\n relationship,\n () =>\n `Exists: relationship \"${this.#relationshipName}\" not found on node`,\n );\n let size = 0;\n for (const n of relationship()) {\n if (n === 'yield') {\n yield 'yield';\n } else {\n size++;\n }\n }\n return size;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"exists.js","names":["#input","#relationshipName","#not","#parentJoinKey","#noSizeReuse","#cache","#cacheHitCountsForTesting","#output","#inPush","#getCacheKey","#fetchExists","#filter","#pushWithFilter","#fetchSize"],"sources":["../../../../../zql/src/ivm/exists.ts"],"sourcesContent":["import {areEqual} from '../../../shared/src/arrays.ts';\nimport {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport type {CompoundKey} from '../../../zero-protocol/src/ast.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {makeAddChange, makeRemoveChange, type Change} from './change.ts';\nimport {normalizeUndefined, type Node, type NormalizedValue} from './data.ts';\nimport {\n throwFilterOutput,\n type FilterInput,\n type FilterOperator,\n type FilterOutput,\n} from './filter-operators.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\n/**\n * The Exists operator filters data based on whether or not a relationship is\n * non-empty.\n */\nexport class Exists implements FilterOperator {\n readonly #input: FilterInput;\n readonly #relationshipName: string;\n readonly #not: boolean;\n readonly #parentJoinKey: CompoundKey;\n readonly #noSizeReuse: boolean;\n #cache: Map<string, boolean>;\n #cacheHitCountsForTesting: Map<string, number> | undefined;\n #output: FilterOutput = throwFilterOutput;\n\n /**\n * This instance variable is `true` when this operator is processing a `push`,\n * and is used to disable reuse of cached sizes across rows with the\n * same parent join key value.\n * This is necessary because during a push relationships can be inconsistent\n * due to push communicating changes (which may change multiple Nodes) one\n * Node at a time.\n */\n #inPush = false;\n\n constructor(\n input: FilterInput,\n relationshipName: string,\n parentJoinKey: CompoundKey,\n type: 'EXISTS' | 'NOT EXISTS',\n cacheHitCountsForTesting?: Map<string, number>,\n ) {\n this.#input = input;\n this.#relationshipName = relationshipName;\n this.#input.setFilterOutput(this);\n this.#cache = new Map();\n this.#cacheHitCountsForTesting = cacheHitCountsForTesting;\n assert(\n this.#input.getSchema().relationships[relationshipName],\n `Input schema missing ${relationshipName}`,\n );\n this.#not = type === 'NOT EXISTS';\n this.#parentJoinKey = parentJoinKey;\n\n // If the parentJoinKey is the primary key, no sense in trying to reuse.\n this.#noSizeReuse = areEqual(\n parentJoinKey,\n this.#input.getSchema().primaryKey,\n );\n }\n\n setFilterOutput(output: FilterOutput): void {\n this.#output = output;\n }\n\n beginFilter() {\n this.#output.beginFilter();\n }\n\n endFilter() {\n this.#cache = new Map();\n this.#output.endFilter();\n }\n\n *filter(node: Node): Generator<'yield', boolean> {\n let exists: boolean | undefined;\n if (!this.#noSizeReuse && !this.#inPush) {\n const key = this.#getCacheKey(node, this.#parentJoinKey);\n exists = this.#cache.get(key);\n if (exists === undefined) {\n exists = yield* this.#fetchExists(node);\n this.#cache.set(key, exists);\n } else if (this.#cacheHitCountsForTesting) {\n this.#cacheHitCountsForTesting.set(\n key,\n (this.#cacheHitCountsForTesting.get(key) ?? 0) + 1,\n );\n }\n }\n\n const result =\n (yield* this.#filter(node, exists)) && (yield* this.#output.filter(node));\n return result;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n *push(change: Change): Stream<'yield'> {\n assert(!this.#inPush, 'Unexpected re-entrancy');\n this.#inPush = true;\n try {\n switch (change[ChangeIndex.TYPE]) {\n // add, remove and edit cannot change the size of the\n // this.#relationshipName relationship, so simply #pushWithFilter\n case ChangeType.ADD:\n case ChangeType.EDIT:\n case ChangeType.REMOVE: {\n yield* this.#pushWithFilter(change);\n return;\n }\n case ChangeType.CHILD:\n // Only add and remove child changes for the\n // this.#relationshipName relationship, can change the size\n // of the this.#relationshipName relationship, for other\n // child changes simply #pushWithFilter\n if (\n change[ChangeIndex.CHILD_DATA].relationshipName !==\n this.#relationshipName ||\n change[ChangeIndex.CHILD_DATA].change[ChangeIndex.TYPE] ===\n ChangeType.EDIT ||\n change[ChangeIndex.CHILD_DATA].change[ChangeIndex.TYPE] ===\n ChangeType.CHILD\n ) {\n yield* this.#pushWithFilter(change);\n return;\n }\n switch (change[ChangeIndex.CHILD_DATA].change[ChangeIndex.TYPE]) {\n case ChangeType.ADD: {\n const size = yield* this.#fetchSize(change[ChangeIndex.NODE]);\n if (size === 1) {\n if (this.#not) {\n // Since the add child change currently being processed is not\n // pushed to output, the added child needs to be excluded from\n // the remove being pushed to output (since the child has\n // never been added to the output).\n yield* this.#output.push(\n makeRemoveChange({\n row: change[ChangeIndex.NODE].row,\n relationships: {\n ...change[ChangeIndex.NODE].relationships,\n [this.#relationshipName]: () => [],\n },\n }),\n this,\n );\n } else {\n yield* this.#output.push(\n makeAddChange(change[ChangeIndex.NODE]),\n this,\n );\n }\n } else {\n yield* this.#pushWithFilter(change, size > 0);\n }\n return;\n }\n case ChangeType.REMOVE: {\n const size = yield* this.#fetchSize(change[ChangeIndex.NODE]);\n if (size === 0) {\n if (this.#not) {\n yield* this.#output.push(\n makeAddChange(change[ChangeIndex.NODE]),\n this,\n );\n } else {\n // Since the remove child change currently being processed is\n // not pushed to output, the removed child needs to be added to\n // the remove being pushed to output.\n yield* this.#output.push(\n makeRemoveChange({\n row: change[ChangeIndex.NODE].row,\n relationships: {\n ...change[ChangeIndex.NODE].relationships,\n [this.#relationshipName]: () => [\n change[ChangeIndex.CHILD_DATA].change[\n ChangeIndex.NODE\n ],\n ],\n },\n }),\n this,\n );\n }\n } else {\n yield* this.#pushWithFilter(change, size > 0);\n }\n return;\n }\n }\n return;\n default:\n unreachable(change);\n }\n } finally {\n this.#inPush = false;\n }\n }\n\n /**\n * Returns whether or not the node's this.#relationshipName\n * relationship passes the exist/not exists filter condition.\n * If the optional `size` is passed it is used.\n * Otherwise, if there is a stored size for the row it is used.\n * Otherwise the size is computed by streaming the node's\n * relationship with this.#relationshipName (this computed size is also\n * stored).\n */\n *#filter(node: Node, exists?: boolean): Generator<'yield', boolean> {\n exists = exists ?? (yield* this.#fetchExists(node));\n return this.#not ? !exists : exists;\n }\n\n #getCacheKey(node: Node, def: CompoundKey): string {\n const values: NormalizedValue[] = [];\n for (const key of def) {\n values.push(normalizeUndefined(node.row[key]));\n }\n return JSON.stringify(values);\n }\n\n /**\n * Pushes a change if this.#filter is true for its row.\n */\n *#pushWithFilter(change: Change, exists?: boolean): Stream<'yield'> {\n if (yield* this.#filter(change[ChangeIndex.NODE], exists)) {\n yield* this.#output.push(change, this);\n }\n }\n\n *#fetchExists(node: Node): Generator<'yield', boolean> {\n // While it seems like this should be able to fetch just 1 node\n // to check for exists, we can't because Take does not support\n // early return during initial fetch.\n return (yield* this.#fetchSize(node)) > 0;\n }\n\n *#fetchSize(node: Node): Generator<'yield', number> {\n const relationship = node.relationships[this.#relationshipName];\n assert(\n relationship,\n () =>\n `Exists: relationship \"${this.#relationshipName}\" not found on node`,\n );\n let size = 0;\n for (const n of relationship()) {\n if (n === 'yield') {\n yield 'yield';\n } else {\n size++;\n }\n }\n return size;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fan-in.js","names":["#inputs","#schema","#output","#accumulatedPushes"],"sources":["../../../../../zql/src/ivm/fan-in.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {emptyArray, identity} from '../../../shared/src/sentinels.ts';\nimport type {ChangeType} from './change-type.ts';\nimport {type Change} from './change.ts';\nimport {type Node} from './data.ts';\nimport type {FanOut} from './fan-out.ts';\nimport {\n throwFilterOutput,\n type FilterInput,\n type FilterOperator,\n type FilterOutput,\n} from './filter-operators.ts';\nimport {pushAccumulatedChanges} from './push-accumulated.ts';\nimport type {SourceSchema} from './schema.ts';\n\n/**\n * The FanIn operator merges multiple streams into one.\n * It eliminates duplicates and must be paired with a fan-out operator\n * somewhere upstream of the fan-in.\n *\n * issue\n * |\n * fan-out\n * / \\\n * a b\n * \\ /\n * fan-in\n * |\n */\nexport class FanIn implements FilterOperator {\n readonly #inputs: readonly FilterInput[];\n readonly #schema: SourceSchema;\n #output: FilterOutput = throwFilterOutput;\n #accumulatedPushes: Change[] = [];\n\n constructor(fanOut: FanOut, inputs: FilterInput[]) {\n this.#inputs = inputs;\n this.#schema = fanOut.getSchema();\n for (const input of inputs) {\n input.setFilterOutput(this);\n assert(this.#schema === input.getSchema(), `Schema mismatch in fan-in`);\n }\n }\n\n setFilterOutput(output: FilterOutput): void {\n this.#output = output;\n }\n\n destroy(): void {\n for (const input of this.#inputs) {\n input.destroy();\n }\n }\n\n getSchema() {\n return this.#schema;\n }\n\n beginFilter(): void {\n this.#output.beginFilter();\n }\n\n endFilter(): void {\n this.#output.endFilter();\n }\n\n *filter(node: Node): Generator<'yield', boolean> {\n return yield* this.#output.filter(node);\n }\n\n push(change: Change) {\n this.#accumulatedPushes.push(change);\n return emptyArray;\n }\n\n *fanOutDonePushingToAllBranches(fanOutChangeType: ChangeType) {\n if (this.#inputs.length === 0) {\n assert(\n this.#accumulatedPushes.length === 0,\n 'If there are no inputs then fan-in should not receive any pushes.',\n );\n return;\n }\n\n yield* pushAccumulatedChanges(\n this.#accumulatedPushes,\n this.#output,\n this,\n fanOutChangeType,\n identity,\n identity,\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA6BA,IAAa,QAAb,MAA6C;CAC3C;CACA;CACA,UAAwB;CACxB,qBAA+B,
|
|
1
|
+
{"version":3,"file":"fan-in.js","names":["#inputs","#schema","#output","#accumulatedPushes"],"sources":["../../../../../zql/src/ivm/fan-in.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {emptyArray, identity} from '../../../shared/src/sentinels.ts';\nimport type {ChangeType} from './change-type.ts';\nimport {type Change} from './change.ts';\nimport {type Node} from './data.ts';\nimport type {FanOut} from './fan-out.ts';\nimport {\n throwFilterOutput,\n type FilterInput,\n type FilterOperator,\n type FilterOutput,\n} from './filter-operators.ts';\nimport {pushAccumulatedChanges} from './push-accumulated.ts';\nimport type {SourceSchema} from './schema.ts';\n\n/**\n * The FanIn operator merges multiple streams into one.\n * It eliminates duplicates and must be paired with a fan-out operator\n * somewhere upstream of the fan-in.\n *\n * issue\n * |\n * fan-out\n * / \\\n * a b\n * \\ /\n * fan-in\n * |\n */\nexport class FanIn implements FilterOperator {\n readonly #inputs: readonly FilterInput[];\n readonly #schema: SourceSchema;\n #output: FilterOutput = throwFilterOutput;\n #accumulatedPushes: Change[] = [];\n\n constructor(fanOut: FanOut, inputs: FilterInput[]) {\n this.#inputs = inputs;\n this.#schema = fanOut.getSchema();\n for (const input of inputs) {\n input.setFilterOutput(this);\n assert(this.#schema === input.getSchema(), `Schema mismatch in fan-in`);\n }\n }\n\n setFilterOutput(output: FilterOutput): void {\n this.#output = output;\n }\n\n destroy(): void {\n for (const input of this.#inputs) {\n input.destroy();\n }\n }\n\n getSchema() {\n return this.#schema;\n }\n\n beginFilter(): void {\n this.#output.beginFilter();\n }\n\n endFilter(): void {\n this.#output.endFilter();\n }\n\n *filter(node: Node): Generator<'yield', boolean> {\n return yield* this.#output.filter(node);\n }\n\n push(change: Change) {\n this.#accumulatedPushes.push(change);\n return emptyArray;\n }\n\n *fanOutDonePushingToAllBranches(fanOutChangeType: ChangeType) {\n if (this.#inputs.length === 0) {\n assert(\n this.#accumulatedPushes.length === 0,\n 'If there are no inputs then fan-in should not receive any pushes.',\n );\n return;\n }\n\n yield* pushAccumulatedChanges(\n this.#accumulatedPushes,\n this.#output,\n this,\n fanOutChangeType,\n identity,\n identity,\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA6BA,IAAa,QAAb,MAA6C;CAC3C;CACA;CACA,UAAwB;CACxB,qBAA+B,EAAE;CAEjC,YAAY,QAAgB,QAAuB;AACjD,QAAA,SAAe;AACf,QAAA,SAAe,OAAO,WAAW;AACjC,OAAK,MAAM,SAAS,QAAQ;AAC1B,SAAM,gBAAgB,KAAK;AAC3B,UAAO,MAAA,WAAiB,MAAM,WAAW,EAAE,4BAA4B;;;CAI3E,gBAAgB,QAA4B;AAC1C,QAAA,SAAe;;CAGjB,UAAgB;AACd,OAAK,MAAM,SAAS,MAAA,OAClB,OAAM,SAAS;;CAInB,YAAY;AACV,SAAO,MAAA;;CAGT,cAAoB;AAClB,QAAA,OAAa,aAAa;;CAG5B,YAAkB;AAChB,QAAA,OAAa,WAAW;;CAG1B,CAAC,OAAO,MAAyC;AAC/C,SAAO,OAAO,MAAA,OAAa,OAAO,KAAK;;CAGzC,KAAK,QAAgB;AACnB,QAAA,kBAAwB,KAAK,OAAO;AACpC,SAAO;;CAGT,CAAC,+BAA+B,kBAA8B;AAC5D,MAAI,MAAA,OAAa,WAAW,GAAG;AAC7B,UACE,MAAA,kBAAwB,WAAW,GACnC,oEACD;AACD;;AAGF,SAAO,uBACL,MAAA,mBACA,MAAA,QACA,MACA,kBACA,UACA,SACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fan-out.js","names":["#input","#outputs","#fanIn","#destroyCount"],"sources":["../../../../../zql/src/ivm/fan-out.ts"],"sourcesContent":["import {must} from '../../../shared/src/must.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport type {Change} from './change.ts';\nimport type {Node} from './data.ts';\nimport type {FanIn} from './fan-in.ts';\nimport type {\n FilterInput,\n FilterOperator,\n FilterOutput,\n} from './filter-operators.ts';\n\n/**\n * Forks a stream into multiple streams.\n * Is meant to be paired with a `FanIn` operator which will\n * later merge the forks back together.\n */\nexport class FanOut implements FilterOperator {\n readonly #input: FilterInput;\n readonly #outputs: FilterOutput[] = [];\n #fanIn: FanIn | undefined;\n #destroyCount: number = 0;\n\n constructor(input: FilterInput) {\n this.#input = input;\n input.setFilterOutput(this);\n }\n\n setFanIn(fanIn: FanIn) {\n this.#fanIn = fanIn;\n }\n\n setFilterOutput(output: FilterOutput): void {\n this.#outputs.push(output);\n }\n\n destroy(): void {\n if (this.#destroyCount < this.#outputs.length) {\n ++this.#destroyCount;\n if (this.#destroyCount === this.#outputs.length) {\n this.#input.destroy();\n }\n } else {\n throw new Error('FanOut already destroyed once for each output');\n }\n }\n\n getSchema() {\n return this.#input.getSchema();\n }\n\n beginFilter(): void {\n for (const output of this.#outputs) {\n output.beginFilter();\n }\n }\n\n endFilter(): void {\n for (const output of this.#outputs) {\n output.endFilter();\n }\n }\n\n *filter(node: Node): Generator<'yield', boolean> {\n let result = false;\n for (const output of this.#outputs) {\n result = (yield* output.filter(node)) || result;\n if (result) {\n return true;\n }\n }\n return result;\n }\n\n *push(change: Change) {\n for (const out of this.#outputs) {\n yield* out.push(change, this);\n }\n yield* must(\n this.#fanIn,\n 'fan-out must have a corresponding fan-in set!',\n ).fanOutDonePushingToAllBranches(change[ChangeIndex.TYPE]);\n }\n}\n"],"mappings":";;;;;;;AAgBA,IAAa,SAAb,MAA8C;CAC5C;CACA,WAAoC,
|
|
1
|
+
{"version":3,"file":"fan-out.js","names":["#input","#outputs","#fanIn","#destroyCount"],"sources":["../../../../../zql/src/ivm/fan-out.ts"],"sourcesContent":["import {must} from '../../../shared/src/must.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport type {Change} from './change.ts';\nimport type {Node} from './data.ts';\nimport type {FanIn} from './fan-in.ts';\nimport type {\n FilterInput,\n FilterOperator,\n FilterOutput,\n} from './filter-operators.ts';\n\n/**\n * Forks a stream into multiple streams.\n * Is meant to be paired with a `FanIn` operator which will\n * later merge the forks back together.\n */\nexport class FanOut implements FilterOperator {\n readonly #input: FilterInput;\n readonly #outputs: FilterOutput[] = [];\n #fanIn: FanIn | undefined;\n #destroyCount: number = 0;\n\n constructor(input: FilterInput) {\n this.#input = input;\n input.setFilterOutput(this);\n }\n\n setFanIn(fanIn: FanIn) {\n this.#fanIn = fanIn;\n }\n\n setFilterOutput(output: FilterOutput): void {\n this.#outputs.push(output);\n }\n\n destroy(): void {\n if (this.#destroyCount < this.#outputs.length) {\n ++this.#destroyCount;\n if (this.#destroyCount === this.#outputs.length) {\n this.#input.destroy();\n }\n } else {\n throw new Error('FanOut already destroyed once for each output');\n }\n }\n\n getSchema() {\n return this.#input.getSchema();\n }\n\n beginFilter(): void {\n for (const output of this.#outputs) {\n output.beginFilter();\n }\n }\n\n endFilter(): void {\n for (const output of this.#outputs) {\n output.endFilter();\n }\n }\n\n *filter(node: Node): Generator<'yield', boolean> {\n let result = false;\n for (const output of this.#outputs) {\n result = (yield* output.filter(node)) || result;\n if (result) {\n return true;\n }\n }\n return result;\n }\n\n *push(change: Change) {\n for (const out of this.#outputs) {\n yield* out.push(change, this);\n }\n yield* must(\n this.#fanIn,\n 'fan-out must have a corresponding fan-in set!',\n ).fanOutDonePushingToAllBranches(change[ChangeIndex.TYPE]);\n }\n}\n"],"mappings":";;;;;;;AAgBA,IAAa,SAAb,MAA8C;CAC5C;CACA,WAAoC,EAAE;CACtC;CACA,gBAAwB;CAExB,YAAY,OAAoB;AAC9B,QAAA,QAAc;AACd,QAAM,gBAAgB,KAAK;;CAG7B,SAAS,OAAc;AACrB,QAAA,QAAc;;CAGhB,gBAAgB,QAA4B;AAC1C,QAAA,QAAc,KAAK,OAAO;;CAG5B,UAAgB;AACd,MAAI,MAAA,eAAqB,MAAA,QAAc,QAAQ;AAC7C,KAAE,MAAA;AACF,OAAI,MAAA,iBAAuB,MAAA,QAAc,OACvC,OAAA,MAAY,SAAS;QAGvB,OAAM,IAAI,MAAM,gDAAgD;;CAIpE,YAAY;AACV,SAAO,MAAA,MAAY,WAAW;;CAGhC,cAAoB;AAClB,OAAK,MAAM,UAAU,MAAA,QACnB,QAAO,aAAa;;CAIxB,YAAkB;AAChB,OAAK,MAAM,UAAU,MAAA,QACnB,QAAO,WAAW;;CAItB,CAAC,OAAO,MAAyC;EAC/C,IAAI,SAAS;AACb,OAAK,MAAM,UAAU,MAAA,SAAe;AAClC,aAAU,OAAO,OAAO,OAAO,KAAK,KAAK;AACzC,OAAI,OACF,QAAO;;AAGX,SAAO;;CAGT,CAAC,KAAK,QAAgB;AACpB,OAAK,MAAM,OAAO,MAAA,QAChB,QAAO,IAAI,KAAK,QAAQ,KAAK;AAE/B,SAAO,KACL,MAAA,OACA,gDACD,CAAC,+BAA+B,OAAO,GAAkB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter-operators.js","names":["#input","#output","#start"],"sources":["../../../../../zql/src/ivm/filter-operators.ts"],"sourcesContent":["import type {BuilderDelegate} from '../builder/builder.ts';\nimport type {Change} from './change.ts';\nimport {type Node} from './data.ts';\nimport type {FetchRequest, Input, InputBase, Output} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\n/**\n * The `where` clause of a ZQL query is implemented using a sub-graph of\n * `FilterOperators`. This sub-graph starts with a `FilterStart` operator,\n * that adapts from the normal `Operator` `Output`, to the\n * `FilterOperator` `FilterInput`, and ends with a `FilterEnd` operator that\n * adapts from a `FilterOperator` `FilterOutput` to a normal `Operator` `Input`.\n * `FilterOperator`'s do not have `fetch` instead they have a\n * `filter(node: Node): boolean` method.\n * They also have `push` which is just like normal `Operator` push.\n * Not having a `fetch` means these `FilterOperator`'s cannot modify\n * `Node` `row`s or `relationship`s, but they shouldn't, they should just\n * filter.\n *\n * This `FilterOperator` abstraction enables much more efficient processing of\n * `fetch` for `where` clauses containing OR conditions.\n *\n * See https://github.com/rocicorp/mono/pull/4339\n */\n\nexport interface FilterInput extends InputBase {\n /** Tell the input where to send its output. */\n setFilterOutput(output: FilterOutput): void;\n}\n\nexport interface FilterOutput extends Output {\n // Lets the operator know that we're in a loop of filtering\n // nodes. E.g., so the operator can cache results for the\n // duration of the loop.\n beginFilter(): void;\n filter(node: Node): Generator<'yield', boolean>;\n endFilter(): void;\n}\n\nexport interface FilterOperator extends FilterInput, FilterOutput {}\n\n/**\n * An implementation of FilterOutput that throws if push or filter is called.\n * It is used as the initial value for for an operator's output before it is\n * set.\n */\nexport const throwFilterOutput: FilterOutput = {\n *push(_change: Change): Stream<'yield'> {\n throw new Error('Output not set');\n },\n\n *filter(_node: Node): Generator<'yield', boolean> {\n throw new Error('Output not set');\n },\n\n beginFilter() {},\n endFilter() {},\n};\n\nexport class FilterStart implements FilterInput, Output {\n readonly #input: Input;\n #output: FilterOutput = throwFilterOutput;\n\n constructor(input: Input) {\n this.#input = input;\n input.setOutput(this);\n }\n\n setFilterOutput(output: FilterOutput) {\n this.#output = output;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n *push(change: Change) {\n yield* this.#output.push(change, this);\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n this.#output.beginFilter();\n try {\n for (const node of this.#input.fetch(req)) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n if (yield* this.#output.filter(node)) {\n yield node;\n }\n }\n } finally {\n // finally is important if an exception is thrown or\n // if the stream is not fully consumed.\n this.#output.endFilter();\n }\n }\n}\n\nexport class FilterEnd implements Input, FilterOutput {\n readonly #start: FilterStart;\n readonly #input: FilterInput;\n\n #output: Output = throwFilterOutput;\n\n constructor(start: FilterStart, input: FilterInput) {\n this.#start = start;\n this.#input = input;\n input.setFilterOutput(this);\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n for (const node of this.#start.fetch(req)) {\n yield node;\n }\n }\n\n beginFilter() {}\n endFilter() {}\n\n *filter(_node: Node) {\n return true;\n }\n\n setOutput(output: Output) {\n this.#output = output;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n *push(change: Change) {\n yield* this.#output.push(change, this);\n }\n}\n\nexport function buildFilterPipeline(\n input: Input,\n delegate: BuilderDelegate,\n pipeline: (filterInput: FilterInput) => FilterInput,\n): Input {\n const filterStart = new FilterStart(input);\n delegate.addEdge(input, filterStart);\n const middle = pipeline(filterStart);\n delegate.addEdge(filterStart, middle);\n const filterEnd = new FilterEnd(filterStart, middle);\n delegate.addEdge(middle, filterEnd);\n return filterEnd;\n}\n"],"mappings":";;;;;;;AA+CA,IAAa,oBAAkC;CAC7C,CAAC,KAAK,SAAkC;
|
|
1
|
+
{"version":3,"file":"filter-operators.js","names":["#input","#output","#start"],"sources":["../../../../../zql/src/ivm/filter-operators.ts"],"sourcesContent":["import type {BuilderDelegate} from '../builder/builder.ts';\nimport type {Change} from './change.ts';\nimport {type Node} from './data.ts';\nimport type {FetchRequest, Input, InputBase, Output} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\n/**\n * The `where` clause of a ZQL query is implemented using a sub-graph of\n * `FilterOperators`. This sub-graph starts with a `FilterStart` operator,\n * that adapts from the normal `Operator` `Output`, to the\n * `FilterOperator` `FilterInput`, and ends with a `FilterEnd` operator that\n * adapts from a `FilterOperator` `FilterOutput` to a normal `Operator` `Input`.\n * `FilterOperator`'s do not have `fetch` instead they have a\n * `filter(node: Node): boolean` method.\n * They also have `push` which is just like normal `Operator` push.\n * Not having a `fetch` means these `FilterOperator`'s cannot modify\n * `Node` `row`s or `relationship`s, but they shouldn't, they should just\n * filter.\n *\n * This `FilterOperator` abstraction enables much more efficient processing of\n * `fetch` for `where` clauses containing OR conditions.\n *\n * See https://github.com/rocicorp/mono/pull/4339\n */\n\nexport interface FilterInput extends InputBase {\n /** Tell the input where to send its output. */\n setFilterOutput(output: FilterOutput): void;\n}\n\nexport interface FilterOutput extends Output {\n // Lets the operator know that we're in a loop of filtering\n // nodes. E.g., so the operator can cache results for the\n // duration of the loop.\n beginFilter(): void;\n filter(node: Node): Generator<'yield', boolean>;\n endFilter(): void;\n}\n\nexport interface FilterOperator extends FilterInput, FilterOutput {}\n\n/**\n * An implementation of FilterOutput that throws if push or filter is called.\n * It is used as the initial value for for an operator's output before it is\n * set.\n */\nexport const throwFilterOutput: FilterOutput = {\n *push(_change: Change): Stream<'yield'> {\n throw new Error('Output not set');\n },\n\n *filter(_node: Node): Generator<'yield', boolean> {\n throw new Error('Output not set');\n },\n\n beginFilter() {},\n endFilter() {},\n};\n\nexport class FilterStart implements FilterInput, Output {\n readonly #input: Input;\n #output: FilterOutput = throwFilterOutput;\n\n constructor(input: Input) {\n this.#input = input;\n input.setOutput(this);\n }\n\n setFilterOutput(output: FilterOutput) {\n this.#output = output;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n *push(change: Change) {\n yield* this.#output.push(change, this);\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n this.#output.beginFilter();\n try {\n for (const node of this.#input.fetch(req)) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n if (yield* this.#output.filter(node)) {\n yield node;\n }\n }\n } finally {\n // finally is important if an exception is thrown or\n // if the stream is not fully consumed.\n this.#output.endFilter();\n }\n }\n}\n\nexport class FilterEnd implements Input, FilterOutput {\n readonly #start: FilterStart;\n readonly #input: FilterInput;\n\n #output: Output = throwFilterOutput;\n\n constructor(start: FilterStart, input: FilterInput) {\n this.#start = start;\n this.#input = input;\n input.setFilterOutput(this);\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n for (const node of this.#start.fetch(req)) {\n yield node;\n }\n }\n\n beginFilter() {}\n endFilter() {}\n\n *filter(_node: Node) {\n return true;\n }\n\n setOutput(output: Output) {\n this.#output = output;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n *push(change: Change) {\n yield* this.#output.push(change, this);\n }\n}\n\nexport function buildFilterPipeline(\n input: Input,\n delegate: BuilderDelegate,\n pipeline: (filterInput: FilterInput) => FilterInput,\n): Input {\n const filterStart = new FilterStart(input);\n delegate.addEdge(input, filterStart);\n const middle = pipeline(filterStart);\n delegate.addEdge(filterStart, middle);\n const filterEnd = new FilterEnd(filterStart, middle);\n delegate.addEdge(middle, filterEnd);\n return filterEnd;\n}\n"],"mappings":";;;;;;;AA+CA,IAAa,oBAAkC;CAC7C,CAAC,KAAK,SAAkC;AACtC,QAAM,IAAI,MAAM,iBAAiB;;CAGnC,CAAC,OAAO,OAA0C;AAChD,QAAM,IAAI,MAAM,iBAAiB;;CAGnC,cAAc;CACd,YAAY;CACb;AAED,IAAa,cAAb,MAAwD;CACtD;CACA,UAAwB;CAExB,YAAY,OAAc;AACxB,QAAA,QAAc;AACd,QAAM,UAAU,KAAK;;CAGvB,gBAAgB,QAAsB;AACpC,QAAA,SAAe;;CAGjB,UAAgB;AACd,QAAA,MAAY,SAAS;;CAGvB,YAA0B;AACxB,SAAO,MAAA,MAAY,WAAW;;CAGhC,CAAC,KAAK,QAAgB;AACpB,SAAO,MAAA,OAAa,KAAK,QAAQ,KAAK;;CAGxC,CAAC,MAAM,KAA2C;AAChD,QAAA,OAAa,aAAa;AAC1B,MAAI;AACF,QAAK,MAAM,QAAQ,MAAA,MAAY,MAAM,IAAI,EAAE;AACzC,QAAI,SAAS,SAAS;AACpB,WAAM;AACN;;AAEF,QAAI,OAAO,MAAA,OAAa,OAAO,KAAK,CAClC,OAAM;;YAGF;AAGR,SAAA,OAAa,WAAW;;;;AAK9B,IAAa,YAAb,MAAsD;CACpD;CACA;CAEA,UAAkB;CAElB,YAAY,OAAoB,OAAoB;AAClD,QAAA,QAAc;AACd,QAAA,QAAc;AACd,QAAM,gBAAgB,KAAK;;CAG7B,CAAC,MAAM,KAA2C;AAChD,OAAK,MAAM,QAAQ,MAAA,MAAY,MAAM,IAAI,CACvC,OAAM;;CAIV,cAAc;CACd,YAAY;CAEZ,CAAC,OAAO,OAAa;AACnB,SAAO;;CAGT,UAAU,QAAgB;AACxB,QAAA,SAAe;;CAGjB,UAAgB;AACd,QAAA,MAAY,SAAS;;CAGvB,YAA0B;AACxB,SAAO,MAAA,MAAY,WAAW;;CAGhC,CAAC,KAAK,QAAgB;AACpB,SAAO,MAAA,OAAa,KAAK,QAAQ,KAAK;;;AAI1C,SAAgB,oBACd,OACA,UACA,UACO;CACP,MAAM,cAAc,IAAI,YAAY,MAAM;AAC1C,UAAS,QAAQ,OAAO,YAAY;CACpC,MAAM,SAAS,SAAS,YAAY;AACpC,UAAS,QAAQ,aAAa,OAAO;CACrC,MAAM,YAAY,IAAI,UAAU,aAAa,OAAO;AACpD,UAAS,QAAQ,QAAQ,UAAU;AACnC,QAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter-push.js","names":[],"sources":["../../../../../zql/src/ivm/filter-push.ts"],"sourcesContent":["import {unreachable} from '../../../shared/src/asserts.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport {maybeSplitAndPushEditChange} from './maybe-split-and-push-edit-change.ts';\nimport type {InputBase, Output} from './operator.ts';\nimport type {Stream} from './stream.ts';\n\nexport function* filterPush(\n change: Change,\n output: Output,\n pusher: InputBase,\n predicate?: (row: Row) => boolean,\n): Stream<'yield'> {\n if (!predicate) {\n yield* output.push(change, pusher);\n return;\n }\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n case ChangeType.REMOVE:\n if (predicate(change[ChangeIndex.NODE].row)) {\n yield* output.push(change, pusher);\n }\n break;\n case ChangeType.CHILD:\n if (predicate(change[ChangeIndex.NODE].row)) {\n yield* output.push(change, pusher);\n }\n break;\n case ChangeType.EDIT:\n yield* maybeSplitAndPushEditChange(change, predicate, output, pusher);\n break;\n default:\n unreachable(change);\n }\n}\n"],"mappings":";;;AASA,UAAiB,WACf,QACA,QACA,QACA,WACiB;
|
|
1
|
+
{"version":3,"file":"filter-push.js","names":[],"sources":["../../../../../zql/src/ivm/filter-push.ts"],"sourcesContent":["import {unreachable} from '../../../shared/src/asserts.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport {maybeSplitAndPushEditChange} from './maybe-split-and-push-edit-change.ts';\nimport type {InputBase, Output} from './operator.ts';\nimport type {Stream} from './stream.ts';\n\nexport function* filterPush(\n change: Change,\n output: Output,\n pusher: InputBase,\n predicate?: (row: Row) => boolean,\n): Stream<'yield'> {\n if (!predicate) {\n yield* output.push(change, pusher);\n return;\n }\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n case ChangeType.REMOVE:\n if (predicate(change[ChangeIndex.NODE].row)) {\n yield* output.push(change, pusher);\n }\n break;\n case ChangeType.CHILD:\n if (predicate(change[ChangeIndex.NODE].row)) {\n yield* output.push(change, pusher);\n }\n break;\n case ChangeType.EDIT:\n yield* maybeSplitAndPushEditChange(change, predicate, output, pusher);\n break;\n default:\n unreachable(change);\n }\n}\n"],"mappings":";;;AASA,UAAiB,WACf,QACA,QACA,QACA,WACiB;AACjB,KAAI,CAAC,WAAW;AACd,SAAO,OAAO,KAAK,QAAQ,OAAO;AAClC;;AAEF,SAAQ,OAAO,IAAf;EACE,KAAK;EACL,KAAK;AACH,OAAI,UAAU,OAAO,GAAkB,IAAI,CACzC,QAAO,OAAO,KAAK,QAAQ,OAAO;AAEpC;EACF,KAAK;AACH,OAAI,UAAU,OAAO,GAAkB,IAAI,CACzC,QAAO,OAAO,KAAK,QAAQ,OAAO;AAEpC;EACF,KAAK;AACH,UAAO,4BAA4B,QAAQ,WAAW,QAAQ,OAAO;AACrE;EACF,QACE,aAAY,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter.js","names":["#input","#predicate","#output"],"sources":["../../../../../zql/src/ivm/filter.ts"],"sourcesContent":["import type {Row} from '../../../zero-protocol/src/data.ts';\nimport type {Change} from './change.ts';\nimport {type Node} from './data.ts';\nimport {\n throwFilterOutput,\n type FilterInput,\n type FilterOperator,\n type FilterOutput,\n} from './filter-operators.ts';\nimport {filterPush} from './filter-push.ts';\nimport type {SourceSchema} from './schema.ts';\n\n/**\n * The Filter operator filters data through a predicate. It is stateless.\n *\n * The predicate must be pure.\n */\nexport class Filter implements FilterOperator {\n readonly #input: FilterInput;\n readonly #predicate: (row: Row) => boolean;\n\n #output: FilterOutput = throwFilterOutput;\n\n constructor(input: FilterInput, predicate: (row: Row) => boolean) {\n this.#input = input;\n this.#predicate = predicate;\n input.setFilterOutput(this);\n }\n\n beginFilter(): void {\n this.#output.beginFilter();\n }\n\n endFilter(): void {\n this.#output.endFilter();\n }\n\n *filter(node: Node): Generator<'yield', boolean> {\n return this.#predicate(node.row) && (yield* this.#output.filter(node));\n }\n\n setFilterOutput(output: FilterOutput) {\n this.#output = output;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n *push(change: Change) {\n yield* filterPush(change, this.#output, this, this.#predicate);\n }\n}\n"],"mappings":";;;;;;;;;AAiBA,IAAa,SAAb,MAA8C;CAC5C;CACA;CAEA,UAAwB;CAExB,YAAY,OAAoB,WAAkC;
|
|
1
|
+
{"version":3,"file":"filter.js","names":["#input","#predicate","#output"],"sources":["../../../../../zql/src/ivm/filter.ts"],"sourcesContent":["import type {Row} from '../../../zero-protocol/src/data.ts';\nimport type {Change} from './change.ts';\nimport {type Node} from './data.ts';\nimport {\n throwFilterOutput,\n type FilterInput,\n type FilterOperator,\n type FilterOutput,\n} from './filter-operators.ts';\nimport {filterPush} from './filter-push.ts';\nimport type {SourceSchema} from './schema.ts';\n\n/**\n * The Filter operator filters data through a predicate. It is stateless.\n *\n * The predicate must be pure.\n */\nexport class Filter implements FilterOperator {\n readonly #input: FilterInput;\n readonly #predicate: (row: Row) => boolean;\n\n #output: FilterOutput = throwFilterOutput;\n\n constructor(input: FilterInput, predicate: (row: Row) => boolean) {\n this.#input = input;\n this.#predicate = predicate;\n input.setFilterOutput(this);\n }\n\n beginFilter(): void {\n this.#output.beginFilter();\n }\n\n endFilter(): void {\n this.#output.endFilter();\n }\n\n *filter(node: Node): Generator<'yield', boolean> {\n return this.#predicate(node.row) && (yield* this.#output.filter(node));\n }\n\n setFilterOutput(output: FilterOutput) {\n this.#output = output;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n *push(change: Change) {\n yield* filterPush(change, this.#output, this, this.#predicate);\n }\n}\n"],"mappings":";;;;;;;;;AAiBA,IAAa,SAAb,MAA8C;CAC5C;CACA;CAEA,UAAwB;CAExB,YAAY,OAAoB,WAAkC;AAChE,QAAA,QAAc;AACd,QAAA,YAAkB;AAClB,QAAM,gBAAgB,KAAK;;CAG7B,cAAoB;AAClB,QAAA,OAAa,aAAa;;CAG5B,YAAkB;AAChB,QAAA,OAAa,WAAW;;CAG1B,CAAC,OAAO,MAAyC;AAC/C,SAAO,MAAA,UAAgB,KAAK,IAAI,KAAK,OAAO,MAAA,OAAa,OAAO,KAAK;;CAGvE,gBAAgB,QAAsB;AACpC,QAAA,SAAe;;CAGjB,UAAgB;AACd,QAAA,MAAY,SAAS;;CAGvB,YAA0B;AACxB,SAAO,MAAA,MAAY,WAAW;;CAGhC,CAAC,KAAK,QAAgB;AACpB,SAAO,WAAW,QAAQ,MAAA,QAAc,MAAM,MAAA,UAAgB"}
|
|
@@ -4,9 +4,6 @@ import type { Node } from './data.ts';
|
|
|
4
4
|
import { type FetchRequest, type Input, type Output } from './operator.ts';
|
|
5
5
|
import type { SourceSchema } from './schema.ts';
|
|
6
6
|
import { type Stream } from './stream.ts';
|
|
7
|
-
export declare function getMultiConstraintChunkSize(): number;
|
|
8
|
-
/** Test only. Returns a restore function. */
|
|
9
|
-
export declare function setMultiConstraintChunkSizeForTest(size: number): () => void;
|
|
10
7
|
type Args = {
|
|
11
8
|
parent: Input;
|
|
12
9
|
child: Input;
|
|
@@ -32,6 +29,13 @@ export declare class FlippedJoin implements Input {
|
|
|
32
29
|
getSchema(): SourceSchema;
|
|
33
30
|
fetch(req: FetchRequest): Stream<Node | 'yield'>;
|
|
34
31
|
}
|
|
35
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Canonical string key over `keys` of `record`, used by `#fetchMergeSort`
|
|
34
|
+
* both to dedupe per-child fetches and to map each returned parent row
|
|
35
|
+
* back to the children that referenced its parent-key tuple.
|
|
36
|
+
*
|
|
37
|
+
* Exported for testing.
|
|
38
|
+
*/
|
|
39
|
+
export declare function canonicalKey(record: Record<string, Value | undefined>, keys: CompoundKey): string;
|
|
36
40
|
export {};
|
|
37
41
|
//# sourceMappingURL=flipped-join.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flipped-join.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/flipped-join.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"flipped-join.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/flipped-join.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,WAAW,EAAE,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAC3E,OAAO,KAAK,EAAM,KAAK,EAAC,MAAM,oCAAoC,CAAC;AAenE,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AAQpC,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,MAAM,EACZ,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AAExC,KAAK,IAAI,GAAG;IACV,MAAM,EAAE,KAAK,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IAEb,SAAS,EAAE,WAAW,CAAC;IACvB,QAAQ,EAAE,WAAW,CAAC;IAEtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;GAOG;AACH,qBAAa,WAAY,YAAW,KAAK;;gBAc3B,EACV,MAAM,EACN,KAAK,EACL,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,MAAM,EACN,MAAM,GACP,EAAE,IAAI;IAwCP,OAAO,IAAI,IAAI;IAKf,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,SAAS,IAAI,YAAY;IAIxB,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC;CAyalD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,GAAG,SAAS,CAAC,EACzC,IAAI,EAAE,WAAW,GAChB,MAAM,CAUR"}
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { assert, unreachable } from "../../../shared/src/asserts.js";
|
|
2
2
|
import { binarySearch } from "../../../shared/src/binary-search.js";
|
|
3
|
+
import { must } from "../../../shared/src/must.js";
|
|
3
4
|
import { throwOutput } from "./operator.js";
|
|
4
5
|
import { makeAddChange, makeChildChange, makeEditChange, makeRemoveChange } from "./change.js";
|
|
5
|
-
import { constraintsAreCompatible } from "./constraint.js";
|
|
6
|
+
import { constraintsAreCompatible, keyMatchesPrimaryKey } from "./constraint.js";
|
|
6
7
|
import { buildJoinConstraint, generateWithOverlayNoYield, isJoinMatch, rowEqualsForCompoundKey } from "./join-utils.js";
|
|
7
8
|
import { mergeSortedStreams } from "./memory-source.js";
|
|
8
9
|
//#region ../zql/src/ivm/flipped-join.ts
|
|
9
|
-
var multiConstraintChunkSize = 256;
|
|
10
|
-
function getMultiConstraintChunkSize() {
|
|
11
|
-
return multiConstraintChunkSize;
|
|
12
|
-
}
|
|
13
10
|
/**
|
|
14
11
|
* An *inner* join which fetches nodes from its child input first and then
|
|
15
12
|
* fetches their related nodes from its parent input. Output nodes are the
|
|
@@ -25,6 +22,7 @@ var FlippedJoin = class {
|
|
|
25
22
|
#childKey;
|
|
26
23
|
#relationshipName;
|
|
27
24
|
#schema;
|
|
25
|
+
#parentKeyIsUnique;
|
|
28
26
|
#output = throwOutput;
|
|
29
27
|
#inprogressChildChange;
|
|
30
28
|
#inprogressChildChangePosition;
|
|
@@ -38,6 +36,7 @@ var FlippedJoin = class {
|
|
|
38
36
|
this.#relationshipName = relationshipName;
|
|
39
37
|
const parentSchema = parent.getSchema();
|
|
40
38
|
const childSchema = child.getSchema();
|
|
39
|
+
this.#parentKeyIsUnique = keyMatchesPrimaryKey(parentKey, parentSchema.primaryKey) || (parentSchema.uniqueIndexes?.some((idx) => keyMatchesPrimaryKey(parentKey, idx)) ?? false);
|
|
41
40
|
this.#schema = {
|
|
42
41
|
...parentSchema,
|
|
43
42
|
relationships: {
|
|
@@ -86,75 +85,79 @@ var FlippedJoin = class {
|
|
|
86
85
|
const insertPos = binarySearch(childNodes.length, (i) => compare(removedNode.row, childNodes[i].row));
|
|
87
86
|
childNodes.splice(insertPos, 0, removedNode);
|
|
88
87
|
}
|
|
89
|
-
yield* this.#
|
|
88
|
+
if (this.#parentKeyIsUnique) yield* this.#fetchQuicksort(req, childNodes);
|
|
89
|
+
else yield* this.#fetchMergeSort(req, childNodes);
|
|
90
90
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const
|
|
91
|
+
*#fetchQuicksort(req, childNodes) {
|
|
92
|
+
const pairs = [];
|
|
93
|
+
for (const childNode of childNodes) {
|
|
94
|
+
const constraintFromChild = buildJoinConstraint(childNode.row, this.#childKey, this.#parentKey);
|
|
95
|
+
if (!constraintFromChild || req.constraint && !constraintsAreCompatible(constraintFromChild, req.constraint)) continue;
|
|
96
|
+
const stream = this.#parent.fetch({
|
|
97
|
+
...req,
|
|
98
|
+
constraint: {
|
|
99
|
+
...req.constraint,
|
|
100
|
+
...constraintFromChild
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
for (const node of stream) {
|
|
104
|
+
if (node === "yield") {
|
|
105
|
+
yield "yield";
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
pairs.push({
|
|
109
|
+
childNode,
|
|
110
|
+
parentNode: node
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const compareRows = this.#schema.compareRows;
|
|
115
|
+
const dir = req.reverse ? -1 : 1;
|
|
116
|
+
pairs.sort((a, b) => compareRows(a.parentNode.row, b.parentNode.row) * dir);
|
|
117
|
+
let i = 0;
|
|
118
|
+
while (i < pairs.length) {
|
|
119
|
+
const minParentNode = pairs[i].parentNode;
|
|
120
|
+
const relatedChildNodes = [];
|
|
121
|
+
while (i < pairs.length && compareRows(pairs[i].parentNode.row, minParentNode.row) === 0) {
|
|
122
|
+
relatedChildNodes.push(pairs[i].childNode);
|
|
123
|
+
i++;
|
|
124
|
+
}
|
|
125
|
+
yield* this.#yieldParentWithOverlay(minParentNode, relatedChildNodes);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
*#fetchMergeSort(req, childNodes) {
|
|
116
129
|
const parentKey = this.#parentKey;
|
|
117
|
-
const
|
|
118
|
-
const computedMulti = [];
|
|
130
|
+
const computedKeys = [];
|
|
119
131
|
const childIndexesByKey = /* @__PURE__ */ new Map();
|
|
120
132
|
for (let i = 0; i < childNodes.length; i++) {
|
|
121
|
-
const constraintFromChild = buildJoinConstraint(childNodes[i].row, childKey, parentKey);
|
|
122
|
-
if (!constraintFromChild ||
|
|
133
|
+
const constraintFromChild = buildJoinConstraint(childNodes[i].row, this.#childKey, parentKey);
|
|
134
|
+
if (!constraintFromChild || req.constraint && !constraintsAreCompatible(constraintFromChild, req.constraint)) continue;
|
|
123
135
|
const key = canonicalKey(constraintFromChild, parentKey);
|
|
124
136
|
const existing = childIndexesByKey.get(key);
|
|
125
137
|
if (existing === void 0) {
|
|
126
138
|
childIndexesByKey.set(key, [i]);
|
|
127
|
-
|
|
139
|
+
computedKeys.push(constraintFromChild);
|
|
128
140
|
} else existing.push(i);
|
|
129
141
|
}
|
|
130
|
-
if (
|
|
142
|
+
if (computedKeys.length === 0) return;
|
|
131
143
|
const compareRows = this.#schema.compareRows;
|
|
132
144
|
const compare = req.reverse ? (a, b) => compareRows(b.row, a.row) : (a, b) => compareRows(a.row, b.row);
|
|
133
|
-
const
|
|
134
|
-
const parentStream = computedMulti.length <= multiConstraintChunkSize ? this.#parent.fetch({
|
|
145
|
+
const streams = computedKeys.map((c) => this.#parent.fetch({
|
|
135
146
|
...req,
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
147
|
+
constraint: req.constraint ? {
|
|
148
|
+
...req.constraint,
|
|
149
|
+
...c
|
|
150
|
+
} : c
|
|
151
|
+
}));
|
|
152
|
+
for (const node of mergeSortedStreams(streams, compare)) {
|
|
139
153
|
if (node === "yield") {
|
|
140
154
|
yield "yield";
|
|
141
155
|
continue;
|
|
142
156
|
}
|
|
143
|
-
const
|
|
144
|
-
const idxs = childIndexesByKey.get(key);
|
|
145
|
-
if (idxs === void 0) continue;
|
|
146
|
-
const relatedChildNodes = idxs.map((i) => childNodes[i]);
|
|
157
|
+
const relatedChildNodes = must(childIndexesByKey.get(canonicalKey(node.row, parentKey))).map((i) => childNodes[i]);
|
|
147
158
|
yield* this.#yieldParentWithOverlay(node, relatedChildNodes);
|
|
148
159
|
}
|
|
149
160
|
}
|
|
150
|
-
*#fetchChunked(req, incomingMultis, computedMulti, compare) {
|
|
151
|
-
const chunkStreams = [];
|
|
152
|
-
for (let i = 0; i < computedMulti.length; i += multiConstraintChunkSize) chunkStreams.push(this.#parent.fetch({
|
|
153
|
-
...req,
|
|
154
|
-
multiConstraints: [...incomingMultis, computedMulti.slice(i, i + multiConstraintChunkSize)]
|
|
155
|
-
}));
|
|
156
|
-
yield* mergeSortedStreams(chunkStreams, compare);
|
|
157
|
-
}
|
|
158
161
|
*#yieldParentWithOverlay(minParentNode, relatedChildNodes) {
|
|
159
162
|
let overlaidRelatedChildNodes = relatedChildNodes;
|
|
160
163
|
if (this.#inprogressChildChange && this.#inprogressChildChangePosition && isJoinMatch(this.#inprogressChildChange[1].row, this.#childKey, minParentNode.row, this.#parentKey)) {
|
|
@@ -278,10 +281,11 @@ var FlippedJoin = class {
|
|
|
278
281
|
}
|
|
279
282
|
};
|
|
280
283
|
/**
|
|
281
|
-
* Canonical string key over `keys` of `record`, used by `#
|
|
282
|
-
* both to dedupe
|
|
283
|
-
*
|
|
284
|
-
*
|
|
284
|
+
* Canonical string key over `keys` of `record`, used by `#fetchMergeSort`
|
|
285
|
+
* both to dedupe per-child fetches and to map each returned parent row
|
|
286
|
+
* back to the children that referenced its parent-key tuple.
|
|
287
|
+
*
|
|
288
|
+
* Exported for testing.
|
|
285
289
|
*/
|
|
286
290
|
function canonicalKey(record, keys) {
|
|
287
291
|
if (keys.length === 1) return canonicalValue(record[keys[0]]);
|
|
@@ -302,6 +306,6 @@ function canonicalValue(v) {
|
|
|
302
306
|
return "j" + JSON.stringify(v);
|
|
303
307
|
}
|
|
304
308
|
//#endregion
|
|
305
|
-
export { FlippedJoin
|
|
309
|
+
export { FlippedJoin };
|
|
306
310
|
|
|
307
311
|
//# sourceMappingURL=flipped-join.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flipped-join.js","names":["#parent","#child","#parentKey","#childKey","#relationshipName","#schema","#pushParent","#pushChild","#output","#inprogressChildChange","#fetchBatched","#fetchChunked","#yieldParentWithOverlay","#inprogressChildChangePosition","#pushChildChange"],"sources":["../../../../../zql/src/ivm/flipped-join.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {binarySearch} from '../../../shared/src/binary-search.ts';\nimport type {CompoundKey, System} from '../../../zero-protocol/src/ast.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {\n makeAddChange,\n makeChildChange,\n makeEditChange,\n makeRemoveChange,\n type Change,\n} from './change.ts';\nimport {constraintsAreCompatible, type Constraint} from './constraint.ts';\nimport type {Node} from './data.ts';\nimport {\n buildJoinConstraint,\n generateWithOverlayNoYield,\n isJoinMatch,\n rowEqualsForCompoundKey,\n} from './join-utils.ts';\nimport {mergeSortedStreams} from './memory-source.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type MultiConstraint,\n type Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\n/**\n * Maximum number of entries sent in a single batched `parent.fetch`\n * call. Larger child-node sets are split into multiple fetches whose\n * sorted streams are merged in JS.\n *\n * Why bound this:\n * - **Bounded fetch on early termination.** `mergeSortedStreams` primes\n * one row from every chunk before yielding the first output, so all\n * chunks open their cursors up front. Smaller chunks cap the\n * worst-case overfetch when downstream `Take` consumes only a few\n * rows — at chunk N, we may waste up to ~N index seeks before\n * `.return()` propagates.\n * - **Parameter limit.** Well under SQLite's default\n * `SQLITE_MAX_VARIABLE_NUMBER` (32766). Compound keys multiply the\n * parameter count by key length, so we leave headroom.\n *\n * Tested 64/128/256; 256 had the best worst-case across planner suites\n * and perf tests.\n *\n * We should, however, start doing shadow tests of the planner\n * against cloudzero queries.\n */\nconst MULTI_CONSTRAINT_CHUNK_SIZE = 256;\n\n// Mutable test seam — production code reads this via the getter.\nlet multiConstraintChunkSize: number = MULTI_CONSTRAINT_CHUNK_SIZE;\n\nexport function getMultiConstraintChunkSize(): number {\n return multiConstraintChunkSize;\n}\n\n/** Test only. Returns a restore function. */\nexport function setMultiConstraintChunkSizeForTest(size: number): () => void {\n const prev = multiConstraintChunkSize;\n multiConstraintChunkSize = size;\n return () => {\n multiConstraintChunkSize = prev;\n };\n}\n\ntype Args = {\n parent: Input;\n child: Input;\n // The nth key in childKey corresponds to the nth key in parentKey.\n parentKey: CompoundKey;\n childKey: CompoundKey;\n\n relationshipName: string;\n hidden: boolean;\n system: System;\n};\n\n/**\n * An *inner* join which fetches nodes from its child input first and then\n * fetches their related nodes from its parent input. Output nodes are the\n * nodes from parent input (in parent input order), which have at least one\n * related child. These output nodes have a new relationship added to them,\n * which has the name `relationshipName`. The value of the relationship is a\n * stream of related nodes from the child input (in child input order).\n */\nexport class FlippedJoin implements Input {\n readonly #parent: Input;\n readonly #child: Input;\n readonly #parentKey: CompoundKey;\n readonly #childKey: CompoundKey;\n readonly #relationshipName: string;\n readonly #schema: SourceSchema;\n\n #output: Output = throwOutput;\n\n #inprogressChildChange: Change | undefined;\n #inprogressChildChangePosition: Row | undefined;\n\n constructor({\n parent,\n child,\n parentKey,\n childKey,\n relationshipName,\n hidden,\n system,\n }: Args) {\n assert(parent !== child, 'Parent and child must be different operators');\n assert(\n parentKey.length === childKey.length,\n 'The parentKey and childKey keys must have same length',\n );\n this.#parent = parent;\n this.#child = child;\n this.#parentKey = parentKey;\n this.#childKey = childKey;\n this.#relationshipName = relationshipName;\n\n const parentSchema = parent.getSchema();\n const childSchema = child.getSchema();\n this.#schema = {\n ...parentSchema,\n relationships: {\n ...parentSchema.relationships,\n [relationshipName]: {\n ...childSchema,\n isHidden: hidden,\n system,\n },\n },\n };\n\n parent.setOutput({\n push: (change: Change) => this.#pushParent(change),\n });\n child.setOutput({\n push: (change: Change) => this.#pushChild(change),\n });\n }\n\n destroy(): void {\n this.#child.destroy();\n this.#parent.destroy();\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n // Translate constraints for the parent on parts of the join key to\n // constraints for the child.\n const childConstraint: Record<string, Value> = {};\n let hasChildConstraint = false;\n if (req.constraint) {\n for (const [key, value] of Object.entries(req.constraint)) {\n const index = this.#parentKey.indexOf(key);\n if (index !== -1) {\n hasChildConstraint = true;\n childConstraint[this.#childKey[index]] = value;\n }\n }\n }\n\n const childNodes: Node[] = [];\n for (const node of this.#child.fetch(\n hasChildConstraint ? {constraint: childConstraint} : {},\n )) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n childNodes.push(node);\n }\n\n // FlippedJoin's split-push change overlay logic is largely\n // the same as Join's with the exception of remove. For remove,\n // the change is undone here, and then re-applied to parents with order\n // less than or equal to change.position below. This is necessary\n // because if the removed node was the last related child, the\n // related parents with position greater than change.position\n // (which should not yet have the node removed), would not even\n // be fetched here, and would be absent from the output all together.\n if (this.#inprogressChildChange?.[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n const removedNode = this.#inprogressChildChange[ChangeIndex.NODE];\n const compare = this.#child.getSchema().compareRows;\n const insertPos = binarySearch(childNodes.length, i =>\n compare(removedNode.row, childNodes[i].row),\n );\n childNodes.splice(insertPos, 0, removedNode);\n }\n\n yield* this.#fetchBatched(req, childNodes);\n }\n\n /**\n * Fetches parents for `childNodes` in batched calls, using\n * `multiConstraint` so the source can issue one query per chunk (e.g.\n * SQL `IN` with index-aware seek) instead of N per-child cursors.\n *\n * Multi-constraint values are split into chunks of `CHUNK_SIZE`, so\n * SQL `IN` lists stay bounded — predictable plans, statement-cache\n * hits across calls of the same chunk size, well below SQLite's\n * parameter limit.\n *\n * Within each chunk, the source returns parents in `compareRows` order.\n * Across chunks, we merge with `mergeSortedStreams` so the overall\n * stream is also in order. Note: the merge primes one row from every\n * chunk before yielding the first output, so all chunks open their\n * cursors up front. Early termination downstream then prevents any\n * further work on un-advanced chunks (cursors get `.return()`'d via\n * `mergeSortedStreams`'s finally block).\n *\n * Replaces the previous split between `#fetchMergeSort` and\n * `#fetchQuicksort`. The unique-vs-not distinction is no longer needed:\n * the source handles cardinality (single index seek for each value) and\n * ordering (SQL `ORDER BY` / index walk).\n */\n *#fetchBatched(\n req: FetchRequest,\n childNodes: Node[],\n ): Stream<Node | 'yield'> {\n const parentReqConstraint = req.constraint;\n const parentKey = this.#parentKey;\n const childKey = this.#childKey;\n\n // Build (deduped) multi-constraint and a key→child-indexes map. Same\n // parent-key value across multiple children groups them together.\n const computedMulti: Constraint[] = [];\n const childIndexesByKey = new Map<string, number[]>();\n for (let i = 0; i < childNodes.length; i++) {\n const constraintFromChild = buildJoinConstraint(\n childNodes[i].row,\n childKey,\n parentKey,\n );\n if (\n !constraintFromChild ||\n (parentReqConstraint &&\n !constraintsAreCompatible(constraintFromChild, parentReqConstraint))\n ) {\n continue;\n }\n const key = canonicalKey(constraintFromChild, parentKey);\n const existing = childIndexesByKey.get(key);\n if (existing === undefined) {\n childIndexesByKey.set(key, [i]);\n computedMulti.push(constraintFromChild);\n } else {\n existing.push(i);\n }\n }\n\n if (computedMulti.length === 0) {\n return;\n }\n\n // Source returns parents in compareRows order within each chunk.\n // Merge across chunks to yield a globally ordered stream.\n const compareRows = this.#schema.compareRows;\n const compare: (a: Node, b: Node) => number = req.reverse\n ? (a, b) => compareRows(b.row, a.row)\n : (a, b) => compareRows(a.row, b.row);\n\n // Append our computed multi to whatever req.multiConstraints already\n // contained — chained FlippedJoins each contribute one entry, so the\n // source ANDs them all (e.g. `assigneeID IN (…) AND creatorID IN (…)`).\n const incoming = req.multiConstraints ?? [];\n const parentStream =\n computedMulti.length <= multiConstraintChunkSize\n ? this.#parent.fetch({\n ...req,\n multiConstraints: [...incoming, computedMulti],\n })\n : this.#fetchChunked(req, incoming, computedMulti, compare);\n\n for (const node of parentStream) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n const key = canonicalKey(node.row, parentKey);\n const idxs = childIndexesByKey.get(key);\n if (idxs === undefined) {\n // This row's parent-key doesn't match any of our computed\n // multi-constraint entries. Happens when our parent is an\n // intermediate operator (e.g. a chained FlippedJoin) that passes\n // multiConstraints through unchanged instead of filtering — see\n // FetchRequest.multiConstraints contract. The lookup miss here\n // performs the required filter, so just skip the row.\n continue;\n }\n // Children retain their original input order within the group\n // because we appended to `idxs` in iteration order.\n const relatedChildNodes: Node[] = idxs.map(i => childNodes[i]);\n yield* this.#yieldParentWithOverlay(node, relatedChildNodes);\n }\n }\n\n *#fetchChunked(\n req: FetchRequest,\n incomingMultis: readonly MultiConstraint[],\n computedMulti: MultiConstraint,\n compare: (a: Node, b: Node) => number,\n ): Stream<Node | 'yield'> {\n const chunkStreams: Stream<Node | 'yield'>[] = [];\n for (let i = 0; i < computedMulti.length; i += multiConstraintChunkSize) {\n chunkStreams.push(\n this.#parent.fetch({\n ...req,\n multiConstraints: [\n ...incomingMultis,\n computedMulti.slice(i, i + multiConstraintChunkSize),\n ],\n }),\n );\n }\n yield* mergeSortedStreams(chunkStreams, compare);\n }\n\n *#yieldParentWithOverlay(\n minParentNode: Node,\n relatedChildNodes: Node[],\n ): Stream<Node> {\n let overlaidRelatedChildNodes = relatedChildNodes;\n if (\n this.#inprogressChildChange &&\n this.#inprogressChildChangePosition &&\n isJoinMatch(\n this.#inprogressChildChange[ChangeIndex.NODE].row,\n this.#childKey,\n minParentNode.row,\n this.#parentKey,\n )\n ) {\n const hasInprogressChildChangeBeenPushedForMinParentNode =\n this.#parent\n .getSchema()\n .compareRows(\n minParentNode.row,\n this.#inprogressChildChangePosition,\n ) <= 0;\n if (this.#inprogressChildChange[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n if (hasInprogressChildChangeBeenPushedForMinParentNode) {\n // Remove from relatedChildNodes since the removed child\n // was inserted into childNodes above.\n overlaidRelatedChildNodes = relatedChildNodes.filter(\n n => n !== this.#inprogressChildChange?.[ChangeIndex.NODE],\n );\n }\n } else if (!hasInprogressChildChangeBeenPushedForMinParentNode) {\n overlaidRelatedChildNodes = [\n ...generateWithOverlayNoYield(\n relatedChildNodes,\n this.#inprogressChildChange,\n this.#child.getSchema(),\n ),\n ];\n }\n }\n\n // yield node if after the overlay it still has relationship nodes\n if (overlaidRelatedChildNodes.length > 0) {\n yield {\n ...minParentNode,\n relationships: {\n ...minParentNode.relationships,\n [this.#relationshipName]: () => overlaidRelatedChildNodes,\n },\n };\n }\n }\n\n *#pushChild(change: Change): Stream<'yield'> {\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n case ChangeType.REMOVE:\n yield* this.#pushChildChange(change);\n break;\n case ChangeType.EDIT: {\n assert(\n rowEqualsForCompoundKey(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n this.#childKey,\n ),\n `Child edit must not change relationship.`,\n );\n yield* this.#pushChildChange(change, true);\n break;\n }\n case ChangeType.CHILD:\n yield* this.#pushChildChange(change, true);\n break;\n }\n }\n\n *#pushChildChange(change: Change, exists?: boolean): Stream<'yield'> {\n this.#inprogressChildChange = change;\n this.#inprogressChildChangePosition = undefined;\n try {\n const constraint = buildJoinConstraint(\n change[ChangeIndex.NODE].row,\n this.#childKey,\n this.#parentKey,\n );\n const parentNodeStream = constraint\n ? this.#parent.fetch({constraint})\n : [];\n for (const parentNode of parentNodeStream) {\n if (parentNode === 'yield') {\n yield 'yield';\n continue;\n }\n this.#inprogressChildChange = change;\n this.#inprogressChildChangePosition = parentNode.row;\n const childNodeStream = () => {\n const constraint = buildJoinConstraint(\n parentNode.row,\n this.#parentKey,\n this.#childKey,\n );\n return constraint ? this.#child.fetch({constraint}) : [];\n };\n if (!exists) {\n for (const childNode of childNodeStream()) {\n if (childNode === 'yield') {\n yield 'yield';\n continue;\n }\n if (\n this.#child\n .getSchema()\n .compareRows(childNode.row, change[ChangeIndex.NODE].row) !== 0\n ) {\n exists = true;\n break;\n }\n }\n }\n if (exists) {\n yield* this.#output.push(\n makeChildChange(\n {\n ...parentNode,\n relationships: {\n ...parentNode.relationships,\n [this.#relationshipName]: childNodeStream,\n },\n },\n {\n relationshipName: this.#relationshipName,\n change,\n },\n ),\n this,\n );\n } else {\n const newNode = {\n ...parentNode,\n relationships: {\n ...parentNode.relationships,\n [this.#relationshipName]: () => [change[ChangeIndex.NODE]],\n },\n };\n yield* this.#output.push(\n change[ChangeIndex.TYPE] === ChangeType.ADD\n ? makeAddChange(newNode)\n : makeRemoveChange(newNode),\n this,\n );\n }\n }\n } finally {\n this.#inprogressChildChange = undefined;\n }\n }\n\n *#pushParent(change: Change): Stream<'yield'> {\n const childNodeStream = (node: Node) => () => {\n const constraint = buildJoinConstraint(\n node.row,\n this.#parentKey,\n this.#childKey,\n );\n return constraint ? this.#child.fetch({constraint}) : [];\n };\n\n const flip = (node: Node) => ({\n ...node,\n relationships: {\n ...node.relationships,\n [this.#relationshipName]: childNodeStream(node),\n },\n });\n\n // If no related child don't push as this is an inner join.\n let hasRelatedChild = false;\n for (const node of childNodeStream(change[ChangeIndex.NODE])()) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n } else {\n hasRelatedChild = true;\n break;\n }\n }\n if (!hasRelatedChild) {\n return;\n }\n\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n yield* this.#output.push(\n makeAddChange(flip(change[ChangeIndex.NODE])),\n this,\n );\n break;\n case ChangeType.REMOVE:\n yield* this.#output.push(\n makeRemoveChange(flip(change[ChangeIndex.NODE])),\n this,\n );\n break;\n case ChangeType.CHILD: {\n yield* this.#output.push(\n makeChildChange(\n flip(change[ChangeIndex.NODE]),\n change[ChangeIndex.CHILD_DATA],\n ),\n this,\n );\n break;\n }\n case ChangeType.EDIT: {\n assert(\n rowEqualsForCompoundKey(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n this.#parentKey,\n ),\n `Parent edit must not change relationship.`,\n );\n yield* this.#output.push(\n makeEditChange(\n flip(change[ChangeIndex.NODE]),\n flip(change[ChangeIndex.OLD_NODE]),\n ),\n this,\n );\n break;\n }\n default:\n unreachable(change);\n }\n }\n}\n\n// Test seam with a widened record type — canonicalValue handles bigint\n// at runtime (zqlite's safeIntegers) but `Value` doesn't list it.\nexport function canonicalKeyForTest(\n record: Record<string, Value | bigint | undefined>,\n keys: CompoundKey,\n): string {\n return canonicalKey(record as Record<string, Value | undefined>, keys);\n}\n\n/**\n * Canonical string key over `keys` of `record`, used by `#fetchBatched`\n * both to dedupe `multiConstraint` entries (record = Constraint) and to\n * map each returned parent row back to the children that referenced its\n * parent-key tuple (record = Row).\n */\nfunction canonicalKey(\n record: Record<string, Value | undefined>,\n keys: CompoundKey,\n): string {\n if (keys.length === 1) {\n return canonicalValue(record[keys[0]]);\n }\n let s = '';\n for (let i = 0; i < keys.length; i++) {\n if (i > 0) s += '\\x00';\n s += canonicalValue(record[keys[i]]);\n }\n return s;\n}\n\nfunction canonicalValue(v: Value | bigint | undefined): string {\n // Tag by type so we don't conflate e.g. `1` (number) with `\"1\"` (string).\n // Bigint shows up at runtime when zqlite's safeIntegers is on, even\n // though the static `Value` type doesn't list it.\n if (v === null || v === undefined) return 'n';\n const t = typeof v;\n if (t === 'string') return 's' + (v as string);\n if (t === 'number') return 'd' + (v as number);\n if (t === 'bigint') return 'b' + (v as bigint).toString();\n if (t === 'boolean') return v ? 't' : 'f';\n return 'j' + JSON.stringify(v);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"flipped-join.js","names":["#parent","#child","#parentKey","#childKey","#relationshipName","#schema","#parentKeyIsUnique","#pushParent","#pushChild","#output","#inprogressChildChange","#fetchQuicksort","#fetchMergeSort","#yieldParentWithOverlay","#inprogressChildChangePosition","#pushChildChange"],"sources":["../../../../../zql/src/ivm/flipped-join.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {binarySearch} from '../../../shared/src/binary-search.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {CompoundKey, System} from '../../../zero-protocol/src/ast.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {\n makeAddChange,\n makeChildChange,\n makeEditChange,\n makeRemoveChange,\n type Change,\n} from './change.ts';\nimport {\n constraintsAreCompatible,\n keyMatchesPrimaryKey,\n type Constraint,\n} from './constraint.ts';\nimport type {Node} from './data.ts';\nimport {\n buildJoinConstraint,\n generateWithOverlayNoYield,\n isJoinMatch,\n rowEqualsForCompoundKey,\n} from './join-utils.ts';\nimport {mergeSortedStreams} from './memory-source.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\ntype Args = {\n parent: Input;\n child: Input;\n // The nth key in childKey corresponds to the nth key in parentKey.\n parentKey: CompoundKey;\n childKey: CompoundKey;\n\n relationshipName: string;\n hidden: boolean;\n system: System;\n};\n\n/**\n * An *inner* join which fetches nodes from its child input first and then\n * fetches their related nodes from its parent input. Output nodes are the\n * nodes from parent input (in parent input order), which have at least one\n * related child. These output nodes have a new relationship added to them,\n * which has the name `relationshipName`. The value of the relationship is a\n * stream of related nodes from the child input (in child input order).\n */\nexport class FlippedJoin implements Input {\n readonly #parent: Input;\n readonly #child: Input;\n readonly #parentKey: CompoundKey;\n readonly #childKey: CompoundKey;\n readonly #relationshipName: string;\n readonly #schema: SourceSchema;\n readonly #parentKeyIsUnique: boolean;\n\n #output: Output = throwOutput;\n\n #inprogressChildChange: Change | undefined;\n #inprogressChildChangePosition: Row | undefined;\n\n constructor({\n parent,\n child,\n parentKey,\n childKey,\n relationshipName,\n hidden,\n system,\n }: Args) {\n assert(parent !== child, 'Parent and child must be different operators');\n assert(\n parentKey.length === childKey.length,\n 'The parentKey and childKey keys must have same length',\n );\n this.#parent = parent;\n this.#child = child;\n this.#parentKey = parentKey;\n this.#childKey = childKey;\n this.#relationshipName = relationshipName;\n\n const parentSchema = parent.getSchema();\n const childSchema = child.getSchema();\n this.#parentKeyIsUnique =\n keyMatchesPrimaryKey(parentKey, parentSchema.primaryKey) ||\n (parentSchema.uniqueIndexes?.some(idx =>\n keyMatchesPrimaryKey(parentKey, idx),\n ) ??\n false);\n this.#schema = {\n ...parentSchema,\n relationships: {\n ...parentSchema.relationships,\n [relationshipName]: {\n ...childSchema,\n isHidden: hidden,\n system,\n },\n },\n };\n\n parent.setOutput({\n push: (change: Change) => this.#pushParent(change),\n });\n child.setOutput({\n push: (change: Change) => this.#pushChild(change),\n });\n }\n\n destroy(): void {\n this.#child.destroy();\n this.#parent.destroy();\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n // Translate constraints for the parent on parts of the join key to\n // constraints for the child.\n const childConstraint: Record<string, Value> = {};\n let hasChildConstraint = false;\n if (req.constraint) {\n for (const [key, value] of Object.entries(req.constraint)) {\n const index = this.#parentKey.indexOf(key);\n if (index !== -1) {\n hasChildConstraint = true;\n childConstraint[this.#childKey[index]] = value;\n }\n }\n }\n\n const childNodes: Node[] = [];\n for (const node of this.#child.fetch(\n hasChildConstraint ? {constraint: childConstraint} : {},\n )) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n childNodes.push(node);\n }\n\n // FlippedJoin's split-push change overlay logic is largely\n // the same as Join's with the exception of remove. For remove,\n // the change is undone here, and then re-applied to parents with order\n // less than or equal to change.position below. This is necessary\n // because if the removed node was the last related child, the\n // related parents with position greater than change.position\n // (which should not yet have the node removed), would not even\n // be fetched here, and would be absent from the output all together.\n if (this.#inprogressChildChange?.[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n const removedNode = this.#inprogressChildChange[ChangeIndex.NODE];\n const compare = this.#child.getSchema().compareRows;\n const insertPos = binarySearch(childNodes.length, i =>\n compare(removedNode.row, childNodes[i].row),\n );\n childNodes.splice(insertPos, 0, removedNode);\n }\n\n if (this.#parentKeyIsUnique) {\n yield* this.#fetchQuicksort(req, childNodes);\n } else {\n yield* this.#fetchMergeSort(req, childNodes);\n }\n }\n\n // When parentKey matches a unique index on the parent (primary or\n // otherwise) each child -> parent fetch returns at most one row, so the\n // merge-sort degenerates to N simultaneous prepared-statement iterators\n // each holding a single-row cursor. Instead, fetch sequentially (letting\n // the statement cache reuse a single prepared statement) and sort the\n // resulting parents into order.\n *#fetchQuicksort(\n req: FetchRequest,\n childNodes: Node[],\n ): Stream<Node | 'yield'> {\n const pairs: {childNode: Node; parentNode: Node}[] = [];\n for (const childNode of childNodes) {\n const constraintFromChild = buildJoinConstraint(\n childNode.row,\n this.#childKey,\n this.#parentKey,\n );\n if (\n !constraintFromChild ||\n (req.constraint &&\n !constraintsAreCompatible(constraintFromChild, req.constraint))\n ) {\n continue;\n }\n const stream = this.#parent.fetch({\n ...req,\n constraint: {\n ...req.constraint,\n ...constraintFromChild,\n },\n });\n // parentKey matches a unique index, so this fetch returns at most\n // one row under the Source contract. Iterate to completion rather\n // than breaking to preserve yield propagation and to avoid silently\n // changing behavior if a source ever returns more.\n for (const node of stream) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n pairs.push({childNode, parentNode: node});\n }\n }\n\n const compareRows = this.#schema.compareRows;\n const dir = req.reverse ? -1 : 1;\n pairs.sort((a, b) => compareRows(a.parentNode.row, b.parentNode.row) * dir);\n\n // Group consecutive pairs with equal parent rows. Array.sort is stable,\n // and childNodes was already in child order, so children within each\n // group retain child order.\n let i = 0;\n while (i < pairs.length) {\n const minParentNode = pairs[i].parentNode;\n const relatedChildNodes: Node[] = [];\n while (\n i < pairs.length &&\n compareRows(pairs[i].parentNode.row, minParentNode.row) === 0\n ) {\n relatedChildNodes.push(pairs[i].childNode);\n i++;\n }\n yield* this.#yieldParentWithOverlay(minParentNode, relatedChildNodes);\n }\n }\n\n *#fetchMergeSort(\n req: FetchRequest,\n childNodes: Node[],\n ): Stream<Node | 'yield'> {\n // Group children by parent-key value so children sharing a value\n // share one fetch (and one cursor). Without this, two children with\n // the same parent-key value would each open their own iterator that\n // re-fetches the same parent rows — wasted IO.\n const parentKey = this.#parentKey;\n const computedKeys: Constraint[] = [];\n const childIndexesByKey = new Map<string, number[]>();\n for (let i = 0; i < childNodes.length; i++) {\n const constraintFromChild = buildJoinConstraint(\n childNodes[i].row,\n this.#childKey,\n parentKey,\n );\n if (\n !constraintFromChild ||\n (req.constraint &&\n !constraintsAreCompatible(constraintFromChild, req.constraint))\n ) {\n continue;\n }\n const key = canonicalKey(constraintFromChild, parentKey);\n const existing = childIndexesByKey.get(key);\n if (existing === undefined) {\n childIndexesByKey.set(key, [i]);\n computedKeys.push(constraintFromChild);\n } else {\n existing.push(i);\n }\n }\n\n if (computedKeys.length === 0) {\n return;\n }\n\n const compareRows = this.#schema.compareRows;\n const compare: (a: Node, b: Node) => number = req.reverse\n ? (a, b) => compareRows(b.row, a.row)\n : (a, b) => compareRows(a.row, b.row);\n\n // One stream per unique parent-key value. Each stream returns its\n // matching parent rows in compareRows order; the heap merges them\n // into a globally ordered stream. Distinct rows can't compare equal\n // (compareRows includes the primary key), so no tie handling needed\n // — every emit maps back to exactly one entry in childIndexesByKey.\n const streams: Stream<Node | 'yield'>[] = computedKeys.map(c =>\n this.#parent.fetch({\n ...req,\n constraint: req.constraint ? {...req.constraint, ...c} : c,\n }),\n );\n\n for (const node of mergeSortedStreams(streams, compare)) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n // Every fetched parent row matches the constraint for one entry\n // in `computedKeys`, whose canonical key was inserted into the\n // map — so the lookup is guaranteed to hit. Children retain\n // their original input order within the group because we\n // appended to the indexes array in iteration order.\n const idxs = must(\n childIndexesByKey.get(canonicalKey(node.row, parentKey)),\n );\n const relatedChildNodes: Node[] = idxs.map(i => childNodes[i]);\n yield* this.#yieldParentWithOverlay(node, relatedChildNodes);\n }\n }\n\n *#yieldParentWithOverlay(\n minParentNode: Node,\n relatedChildNodes: Node[],\n ): Stream<Node> {\n let overlaidRelatedChildNodes = relatedChildNodes;\n if (\n this.#inprogressChildChange &&\n this.#inprogressChildChangePosition &&\n isJoinMatch(\n this.#inprogressChildChange[ChangeIndex.NODE].row,\n this.#childKey,\n minParentNode.row,\n this.#parentKey,\n )\n ) {\n const hasInprogressChildChangeBeenPushedForMinParentNode =\n this.#parent\n .getSchema()\n .compareRows(\n minParentNode.row,\n this.#inprogressChildChangePosition,\n ) <= 0;\n if (this.#inprogressChildChange[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n if (hasInprogressChildChangeBeenPushedForMinParentNode) {\n // Remove from relatedChildNodes since the removed child\n // was inserted into childNodes above.\n overlaidRelatedChildNodes = relatedChildNodes.filter(\n n => n !== this.#inprogressChildChange?.[ChangeIndex.NODE],\n );\n }\n } else if (!hasInprogressChildChangeBeenPushedForMinParentNode) {\n overlaidRelatedChildNodes = [\n ...generateWithOverlayNoYield(\n relatedChildNodes,\n this.#inprogressChildChange,\n this.#child.getSchema(),\n ),\n ];\n }\n }\n\n // yield node if after the overlay it still has relationship nodes\n if (overlaidRelatedChildNodes.length > 0) {\n yield {\n ...minParentNode,\n relationships: {\n ...minParentNode.relationships,\n [this.#relationshipName]: () => overlaidRelatedChildNodes,\n },\n };\n }\n }\n\n *#pushChild(change: Change): Stream<'yield'> {\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n case ChangeType.REMOVE:\n yield* this.#pushChildChange(change);\n break;\n case ChangeType.EDIT: {\n assert(\n rowEqualsForCompoundKey(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n this.#childKey,\n ),\n `Child edit must not change relationship.`,\n );\n yield* this.#pushChildChange(change, true);\n break;\n }\n case ChangeType.CHILD:\n yield* this.#pushChildChange(change, true);\n break;\n }\n }\n\n *#pushChildChange(change: Change, exists?: boolean): Stream<'yield'> {\n this.#inprogressChildChange = change;\n this.#inprogressChildChangePosition = undefined;\n try {\n const constraint = buildJoinConstraint(\n change[ChangeIndex.NODE].row,\n this.#childKey,\n this.#parentKey,\n );\n const parentNodeStream = constraint\n ? this.#parent.fetch({constraint})\n : [];\n for (const parentNode of parentNodeStream) {\n if (parentNode === 'yield') {\n yield 'yield';\n continue;\n }\n this.#inprogressChildChange = change;\n this.#inprogressChildChangePosition = parentNode.row;\n const childNodeStream = () => {\n const constraint = buildJoinConstraint(\n parentNode.row,\n this.#parentKey,\n this.#childKey,\n );\n return constraint ? this.#child.fetch({constraint}) : [];\n };\n if (!exists) {\n for (const childNode of childNodeStream()) {\n if (childNode === 'yield') {\n yield 'yield';\n continue;\n }\n if (\n this.#child\n .getSchema()\n .compareRows(childNode.row, change[ChangeIndex.NODE].row) !== 0\n ) {\n exists = true;\n break;\n }\n }\n }\n if (exists) {\n yield* this.#output.push(\n makeChildChange(\n {\n ...parentNode,\n relationships: {\n ...parentNode.relationships,\n [this.#relationshipName]: childNodeStream,\n },\n },\n {\n relationshipName: this.#relationshipName,\n change,\n },\n ),\n this,\n );\n } else {\n const newNode = {\n ...parentNode,\n relationships: {\n ...parentNode.relationships,\n [this.#relationshipName]: () => [change[ChangeIndex.NODE]],\n },\n };\n yield* this.#output.push(\n change[ChangeIndex.TYPE] === ChangeType.ADD\n ? makeAddChange(newNode)\n : makeRemoveChange(newNode),\n this,\n );\n }\n }\n } finally {\n this.#inprogressChildChange = undefined;\n }\n }\n\n *#pushParent(change: Change): Stream<'yield'> {\n const childNodeStream = (node: Node) => () => {\n const constraint = buildJoinConstraint(\n node.row,\n this.#parentKey,\n this.#childKey,\n );\n return constraint ? this.#child.fetch({constraint}) : [];\n };\n\n const flip = (node: Node) => ({\n ...node,\n relationships: {\n ...node.relationships,\n [this.#relationshipName]: childNodeStream(node),\n },\n });\n\n // If no related child don't push as this is an inner join.\n let hasRelatedChild = false;\n for (const node of childNodeStream(change[ChangeIndex.NODE])()) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n } else {\n hasRelatedChild = true;\n break;\n }\n }\n if (!hasRelatedChild) {\n return;\n }\n\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n yield* this.#output.push(\n makeAddChange(flip(change[ChangeIndex.NODE])),\n this,\n );\n break;\n case ChangeType.REMOVE:\n yield* this.#output.push(\n makeRemoveChange(flip(change[ChangeIndex.NODE])),\n this,\n );\n break;\n case ChangeType.CHILD: {\n yield* this.#output.push(\n makeChildChange(\n flip(change[ChangeIndex.NODE]),\n change[ChangeIndex.CHILD_DATA],\n ),\n this,\n );\n break;\n }\n case ChangeType.EDIT: {\n assert(\n rowEqualsForCompoundKey(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n this.#parentKey,\n ),\n `Parent edit must not change relationship.`,\n );\n yield* this.#output.push(\n makeEditChange(\n flip(change[ChangeIndex.NODE]),\n flip(change[ChangeIndex.OLD_NODE]),\n ),\n this,\n );\n break;\n }\n default:\n unreachable(change);\n }\n }\n}\n\n/**\n * Canonical string key over `keys` of `record`, used by `#fetchMergeSort`\n * both to dedupe per-child fetches and to map each returned parent row\n * back to the children that referenced its parent-key tuple.\n *\n * Exported for testing.\n */\nexport function canonicalKey(\n record: Record<string, Value | undefined>,\n keys: CompoundKey,\n): string {\n if (keys.length === 1) {\n return canonicalValue(record[keys[0]]);\n }\n let s = '';\n for (let i = 0; i < keys.length; i++) {\n if (i > 0) s += '\\x00';\n s += canonicalValue(record[keys[i]]);\n }\n return s;\n}\n\nfunction canonicalValue(v: Value | bigint | undefined): string {\n // Tag by type so we don't conflate e.g. `1` (number) with `\"1\"` (string).\n // Bigint shows up at runtime when zqlite's safeIntegers is on, even\n // though the static `Value` type doesn't list it.\n if (v === null || v === undefined) return 'n';\n const t = typeof v;\n if (t === 'string') return 's' + (v as string);\n if (t === 'number') return 'd' + (v as number);\n if (t === 'bigint') return 'b' + (v as bigint).toString();\n if (t === 'boolean') return v ? 't' : 'f';\n return 'j' + JSON.stringify(v);\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"join-utils.js","names":[],"sources":["../../../../../zql/src/ivm/join-utils.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {CompoundKey} from '../../../zero-protocol/src/ast.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport {compareValues, valuesEqual, type Node} from './data.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\n\nexport function generateWithOverlayNoYield(\n stream: Stream<Node>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node> {\n return generateWithOverlay(stream, overlay, schema) as Stream<Node>;\n}\n\nexport function* generateWithOverlay(\n stream: Stream<Node | 'yield'>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node | 'yield'> {\n let applied = false;\n let editOldApplied = false;\n let editNewApplied = false;\n for (const node of stream) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n let yieldNode = true;\n if (!applied) {\n switch (overlay[ChangeIndex.TYPE]) {\n case ChangeType.ADD: {\n if (\n schema.compareRows(overlay[ChangeIndex.NODE].row, node.row) === 0\n ) {\n applied = true;\n yieldNode = false;\n }\n break;\n }\n case ChangeType.REMOVE: {\n if (schema.compareRows(overlay[ChangeIndex.NODE].row, node.row) < 0) {\n applied = true;\n yield overlay[ChangeIndex.NODE];\n }\n break;\n }\n case ChangeType.EDIT: {\n if (\n !editOldApplied &&\n schema.compareRows(overlay[ChangeIndex.OLD_NODE].row, node.row) < 0\n ) {\n editOldApplied = true;\n if (editNewApplied) {\n applied = true;\n }\n yield overlay[ChangeIndex.OLD_NODE];\n }\n if (\n !editNewApplied &&\n schema.compareRows(overlay[ChangeIndex.NODE].row, node.row) === 0\n ) {\n editNewApplied = true;\n if (editOldApplied) {\n applied = true;\n }\n yieldNode = false;\n }\n break;\n }\n case ChangeType.CHILD: {\n if (\n schema.compareRows(overlay[ChangeIndex.NODE].row, node.row) === 0\n ) {\n applied = true;\n yield {\n row: node.row,\n relationships: {\n ...node.relationships,\n [overlay[ChangeIndex.CHILD_DATA].relationshipName]: () =>\n generateWithOverlay(\n node.relationships[\n overlay[ChangeIndex.CHILD_DATA].relationshipName\n ](),\n overlay[ChangeIndex.CHILD_DATA].change,\n schema.relationships[\n overlay[ChangeIndex.CHILD_DATA].relationshipName\n ],\n ),\n },\n };\n yieldNode = false;\n }\n break;\n }\n }\n }\n if (yieldNode) {\n yield node;\n }\n }\n if (!applied) {\n if (overlay[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n applied = true;\n yield overlay[ChangeIndex.NODE];\n } else if (overlay[ChangeIndex.TYPE] === ChangeType.EDIT) {\n assert(\n editNewApplied,\n 'edit overlay: new node must be applied before old node',\n );\n editOldApplied = true;\n applied = true;\n yield overlay[ChangeIndex.OLD_NODE];\n }\n }\n\n assert(\n applied,\n 'overlayGenerator: overlay was never applied to any fetched node',\n );\n}\n\nexport function generateWithOverlayNoYieldUnordered(\n stream: Stream<Node>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node> {\n return generateWithOverlayUnordered(stream, overlay, schema) as Stream<Node>;\n}\n\nexport function* generateWithOverlayUnordered(\n stream: Stream<Node | 'yield'>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node | 'yield'> {\n // Eager inject\n if (overlay[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n yield overlay[ChangeIndex.NODE];\n } else if (overlay[ChangeIndex.TYPE] === ChangeType.EDIT) {\n yield overlay[ChangeIndex.OLD_NODE];\n }\n\n // Stream with inline suppress\n let suppressed = false;\n for (const node of stream) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n if (!suppressed) {\n if (\n overlay[ChangeIndex.TYPE] === ChangeType.ADD ||\n overlay[ChangeIndex.TYPE] === ChangeType.EDIT\n ) {\n if (\n rowEqualsForCompoundKey(\n overlay[ChangeIndex.NODE].row,\n node.row,\n schema.primaryKey,\n )\n ) {\n suppressed = true;\n continue;\n }\n }\n if (overlay[ChangeIndex.TYPE] === ChangeType.CHILD) {\n if (\n rowEqualsForCompoundKey(\n overlay[ChangeIndex.NODE].row,\n node.row,\n schema.primaryKey,\n )\n ) {\n suppressed = true;\n yield {\n row: node.row,\n relationships: {\n ...node.relationships,\n [overlay[ChangeIndex.CHILD_DATA].relationshipName]: () =>\n generateWithOverlay(\n node.relationships[\n overlay[ChangeIndex.CHILD_DATA].relationshipName\n ](),\n overlay[ChangeIndex.CHILD_DATA].change,\n schema.relationships[\n overlay[ChangeIndex.CHILD_DATA].relationshipName\n ],\n ),\n },\n };\n continue;\n }\n }\n }\n yield node;\n }\n assert(\n suppressed || overlay[ChangeIndex.TYPE] === ChangeType.REMOVE,\n 'overlayGenerator: overlay was never applied to any fetched node',\n );\n}\n\nexport function rowEqualsForCompoundKey(\n a: Row,\n b: Row,\n key: CompoundKey,\n): boolean {\n for (let i = 0; i < key.length; i++) {\n if (compareValues(a[key[i]], b[key[i]]) !== 0) {\n return false;\n }\n }\n return true;\n}\n\nexport function isJoinMatch(\n parent: Row,\n parentKey: CompoundKey,\n child: Row,\n childKey: CompoundKey,\n) {\n for (let i = 0; i < parentKey.length; i++) {\n if (!valuesEqual(parent[parentKey[i]], child[childKey[i]])) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Builds a constraint object by mapping values from `sourceRow` using `sourceKey`\n * to keys specified by `targetKey`. Returns `undefined` if any source value is `null`,\n * since null foreign keys cannot match any rows.\n */\nexport function buildJoinConstraint(\n sourceRow: Row,\n sourceKey: CompoundKey,\n targetKey: CompoundKey,\n): Record<string, Value> | undefined {\n const constraint: Record<string, Value> = {};\n for (let i = 0; i < targetKey.length; i++) {\n const value = sourceRow[sourceKey[i]];\n if (value === null) {\n return undefined;\n }\n constraint[targetKey[i]] = value;\n }\n return constraint;\n}\n"],"mappings":";;;AAUA,SAAgB,2BACd,QACA,SACA,QACc;
|
|
1
|
+
{"version":3,"file":"join-utils.js","names":[],"sources":["../../../../../zql/src/ivm/join-utils.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {CompoundKey} from '../../../zero-protocol/src/ast.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport {compareValues, valuesEqual, type Node} from './data.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\n\nexport function generateWithOverlayNoYield(\n stream: Stream<Node>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node> {\n return generateWithOverlay(stream, overlay, schema) as Stream<Node>;\n}\n\nexport function* generateWithOverlay(\n stream: Stream<Node | 'yield'>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node | 'yield'> {\n let applied = false;\n let editOldApplied = false;\n let editNewApplied = false;\n for (const node of stream) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n let yieldNode = true;\n if (!applied) {\n switch (overlay[ChangeIndex.TYPE]) {\n case ChangeType.ADD: {\n if (\n schema.compareRows(overlay[ChangeIndex.NODE].row, node.row) === 0\n ) {\n applied = true;\n yieldNode = false;\n }\n break;\n }\n case ChangeType.REMOVE: {\n if (schema.compareRows(overlay[ChangeIndex.NODE].row, node.row) < 0) {\n applied = true;\n yield overlay[ChangeIndex.NODE];\n }\n break;\n }\n case ChangeType.EDIT: {\n if (\n !editOldApplied &&\n schema.compareRows(overlay[ChangeIndex.OLD_NODE].row, node.row) < 0\n ) {\n editOldApplied = true;\n if (editNewApplied) {\n applied = true;\n }\n yield overlay[ChangeIndex.OLD_NODE];\n }\n if (\n !editNewApplied &&\n schema.compareRows(overlay[ChangeIndex.NODE].row, node.row) === 0\n ) {\n editNewApplied = true;\n if (editOldApplied) {\n applied = true;\n }\n yieldNode = false;\n }\n break;\n }\n case ChangeType.CHILD: {\n if (\n schema.compareRows(overlay[ChangeIndex.NODE].row, node.row) === 0\n ) {\n applied = true;\n yield {\n row: node.row,\n relationships: {\n ...node.relationships,\n [overlay[ChangeIndex.CHILD_DATA].relationshipName]: () =>\n generateWithOverlay(\n node.relationships[\n overlay[ChangeIndex.CHILD_DATA].relationshipName\n ](),\n overlay[ChangeIndex.CHILD_DATA].change,\n schema.relationships[\n overlay[ChangeIndex.CHILD_DATA].relationshipName\n ],\n ),\n },\n };\n yieldNode = false;\n }\n break;\n }\n }\n }\n if (yieldNode) {\n yield node;\n }\n }\n if (!applied) {\n if (overlay[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n applied = true;\n yield overlay[ChangeIndex.NODE];\n } else if (overlay[ChangeIndex.TYPE] === ChangeType.EDIT) {\n assert(\n editNewApplied,\n 'edit overlay: new node must be applied before old node',\n );\n editOldApplied = true;\n applied = true;\n yield overlay[ChangeIndex.OLD_NODE];\n }\n }\n\n assert(\n applied,\n 'overlayGenerator: overlay was never applied to any fetched node',\n );\n}\n\nexport function generateWithOverlayNoYieldUnordered(\n stream: Stream<Node>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node> {\n return generateWithOverlayUnordered(stream, overlay, schema) as Stream<Node>;\n}\n\nexport function* generateWithOverlayUnordered(\n stream: Stream<Node | 'yield'>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node | 'yield'> {\n // Eager inject\n if (overlay[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n yield overlay[ChangeIndex.NODE];\n } else if (overlay[ChangeIndex.TYPE] === ChangeType.EDIT) {\n yield overlay[ChangeIndex.OLD_NODE];\n }\n\n // Stream with inline suppress\n let suppressed = false;\n for (const node of stream) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n if (!suppressed) {\n if (\n overlay[ChangeIndex.TYPE] === ChangeType.ADD ||\n overlay[ChangeIndex.TYPE] === ChangeType.EDIT\n ) {\n if (\n rowEqualsForCompoundKey(\n overlay[ChangeIndex.NODE].row,\n node.row,\n schema.primaryKey,\n )\n ) {\n suppressed = true;\n continue;\n }\n }\n if (overlay[ChangeIndex.TYPE] === ChangeType.CHILD) {\n if (\n rowEqualsForCompoundKey(\n overlay[ChangeIndex.NODE].row,\n node.row,\n schema.primaryKey,\n )\n ) {\n suppressed = true;\n yield {\n row: node.row,\n relationships: {\n ...node.relationships,\n [overlay[ChangeIndex.CHILD_DATA].relationshipName]: () =>\n generateWithOverlay(\n node.relationships[\n overlay[ChangeIndex.CHILD_DATA].relationshipName\n ](),\n overlay[ChangeIndex.CHILD_DATA].change,\n schema.relationships[\n overlay[ChangeIndex.CHILD_DATA].relationshipName\n ],\n ),\n },\n };\n continue;\n }\n }\n }\n yield node;\n }\n assert(\n suppressed || overlay[ChangeIndex.TYPE] === ChangeType.REMOVE,\n 'overlayGenerator: overlay was never applied to any fetched node',\n );\n}\n\nexport function rowEqualsForCompoundKey(\n a: Row,\n b: Row,\n key: CompoundKey,\n): boolean {\n for (let i = 0; i < key.length; i++) {\n if (compareValues(a[key[i]], b[key[i]]) !== 0) {\n return false;\n }\n }\n return true;\n}\n\nexport function isJoinMatch(\n parent: Row,\n parentKey: CompoundKey,\n child: Row,\n childKey: CompoundKey,\n) {\n for (let i = 0; i < parentKey.length; i++) {\n if (!valuesEqual(parent[parentKey[i]], child[childKey[i]])) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Builds a constraint object by mapping values from `sourceRow` using `sourceKey`\n * to keys specified by `targetKey`. Returns `undefined` if any source value is `null`,\n * since null foreign keys cannot match any rows.\n */\nexport function buildJoinConstraint(\n sourceRow: Row,\n sourceKey: CompoundKey,\n targetKey: CompoundKey,\n): Record<string, Value> | undefined {\n const constraint: Record<string, Value> = {};\n for (let i = 0; i < targetKey.length; i++) {\n const value = sourceRow[sourceKey[i]];\n if (value === null) {\n return undefined;\n }\n constraint[targetKey[i]] = value;\n }\n return constraint;\n}\n"],"mappings":"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"}
|