datahike-browser-tests 1.0.0
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/.circleci/config.yml +405 -0
- package/.circleci/scripts/gen_ci.clj +194 -0
- package/.cirrus.yml +60 -0
- package/.clj-kondo/babashka/sci/config.edn +1 -0
- package/.clj-kondo/babashka/sci/sci/core.clj +9 -0
- package/.clj-kondo/config.edn +95 -0
- package/.dir-locals.el +2 -0
- package/.github/FUNDING.yml +3 -0
- package/.github/ISSUE_TEMPLATE/1-bug-report.yml +68 -0
- package/.github/ISSUE_TEMPLATE/2-feature-request.yml +28 -0
- package/.github/ISSUE_TEMPLATE/config.yml +6 -0
- package/.github/pull_request_template.md +24 -0
- package/.github/workflows/native-image.yml +84 -0
- package/LICENSE +203 -0
- package/README.md +273 -0
- package/bb/deps.edn +9 -0
- package/bb/resources/github-fingerprints +3 -0
- package/bb/resources/native-image-tests/run-bb-pod-tests.clj +162 -0
- package/bb/resources/native-image-tests/run-libdatahike-tests +12 -0
- package/bb/resources/native-image-tests/run-native-image-tests +74 -0
- package/bb/resources/native-image-tests/run-python-tests +22 -0
- package/bb/resources/native-image-tests/testconfig.attr-refs.edn +6 -0
- package/bb/resources/native-image-tests/testconfig.edn +5 -0
- package/bb/resources/template/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
- package/bb/resources/template/.settings/org.eclipse.jdt.core.prefs +9 -0
- package/bb/resources/template/.settings/org.eclipse.m2e.core.prefs +4 -0
- package/bb/resources/template/pom.xml +22 -0
- package/bb/src/tools/build.clj +132 -0
- package/bb/src/tools/clj_kondo.clj +32 -0
- package/bb/src/tools/deploy.clj +26 -0
- package/bb/src/tools/examples.clj +19 -0
- package/bb/src/tools/npm.clj +100 -0
- package/bb/src/tools/python.clj +14 -0
- package/bb/src/tools/release.clj +94 -0
- package/bb/src/tools/test.clj +148 -0
- package/bb/src/tools/version.clj +47 -0
- package/bb.edn +269 -0
- package/benchmark/src/benchmark/cli.clj +195 -0
- package/benchmark/src/benchmark/compare.clj +157 -0
- package/benchmark/src/benchmark/config.clj +316 -0
- package/benchmark/src/benchmark/measure.clj +187 -0
- package/benchmark/src/benchmark/store.clj +190 -0
- package/benchmark/test/benchmark/measure_test.clj +156 -0
- package/build.clj +30 -0
- package/config.edn +49 -0
- package/deps.edn +138 -0
- package/dev/sandbox.clj +82 -0
- package/dev/sandbox.cljs +127 -0
- package/dev/sandbox_benchmarks.clj +27 -0
- package/dev/sandbox_client.clj +87 -0
- package/dev/sandbox_transact_bench.clj +109 -0
- package/dev/user.clj +79 -0
- package/doc/README.md +96 -0
- package/doc/adl/README.md +6 -0
- package/doc/adl/adr-000-adr.org +28 -0
- package/doc/adl/adr-001-attribute-references.org +15 -0
- package/doc/adl/adr-002-build-tooling.org +54 -0
- package/doc/adl/adr-003-db-meta-data.md +52 -0
- package/doc/adl/adr-004-github-flow.md +40 -0
- package/doc/adl/adr-XYZ-template.md +30 -0
- package/doc/adl/index.org +3 -0
- package/doc/assets/datahike-logo.svg +3 -0
- package/doc/assets/datahiking-invoice.org +85 -0
- package/doc/assets/hhtree2.png +0 -0
- package/doc/assets/network_topology.svg +624 -0
- package/doc/assets/perf.png +0 -0
- package/doc/assets/schema_mindmap.mm +132 -0
- package/doc/assets/schema_mindmap.svg +970 -0
- package/doc/assets/temporal_index.mm +74 -0
- package/doc/backend-development.md +78 -0
- package/doc/bb-pod.md +89 -0
- package/doc/benchmarking.md +360 -0
- package/doc/bindings/edn-conversion.md +383 -0
- package/doc/cli.md +162 -0
- package/doc/cljdoc.edn +27 -0
- package/doc/cljs-support.md +133 -0
- package/doc/config.md +406 -0
- package/doc/contributing.md +114 -0
- package/doc/datalog-vs-sql.md +210 -0
- package/doc/datomic_differences.md +109 -0
- package/doc/development/pull-api-ns.md +186 -0
- package/doc/development/pull-frame-state-diagram.jpg +0 -0
- package/doc/distributed.md +566 -0
- package/doc/entity_spec.md +92 -0
- package/doc/gc.md +273 -0
- package/doc/java-api.md +808 -0
- package/doc/javascript-api.md +421 -0
- package/doc/libdatahike.md +86 -0
- package/doc/logging_and_error_handling.md +43 -0
- package/doc/norms.md +66 -0
- package/doc/schema-migration.md +85 -0
- package/doc/schema.md +287 -0
- package/doc/storage-backends.md +363 -0
- package/doc/store-id-refactoring.md +596 -0
- package/doc/time_variance.md +325 -0
- package/doc/unstructured.md +167 -0
- package/doc/versioning.md +261 -0
- package/examples/basic/README.md +19 -0
- package/examples/basic/deps.edn +6 -0
- package/examples/basic/docker-compose.yml +13 -0
- package/examples/basic/src/examples/core.clj +60 -0
- package/examples/basic/src/examples/schema.clj +155 -0
- package/examples/basic/src/examples/store.clj +60 -0
- package/examples/basic/src/examples/time_travel.clj +185 -0
- package/examples/java/.settings/org.eclipse.core.resources.prefs +3 -0
- package/examples/java/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
- package/examples/java/.settings/org.eclipse.jdt.core.prefs +9 -0
- package/examples/java/.settings/org.eclipse.m2e.core.prefs +4 -0
- package/examples/java/README.md +162 -0
- package/examples/java/pom.xml +62 -0
- package/examples/java/src/main/java/examples/QuickStart.java +115 -0
- package/examples/java/src/main/java/examples/SchemaExample.java +148 -0
- package/examples/java/src/main/java/examples/TimeTravelExample.java +121 -0
- package/flake.lock +27 -0
- package/flake.nix +27 -0
- package/http-server/datahike/http/middleware.clj +75 -0
- package/http-server/datahike/http/server.clj +269 -0
- package/java/src/datahike/java/Database.java +274 -0
- package/java/src/datahike/java/Datahike.java +281 -0
- package/java/src/datahike/java/DatahikeGeneratedTest.java +349 -0
- package/java/src/datahike/java/DatahikeTest.java +370 -0
- package/java/src/datahike/java/EDN.java +170 -0
- package/java/src/datahike/java/IEntity.java +11 -0
- package/java/src/datahike/java/Keywords.java +161 -0
- package/java/src/datahike/java/SchemaFlexibility.java +52 -0
- package/java/src/datahike/java/Util.java +219 -0
- package/karma.conf.js +19 -0
- package/libdatahike/compile-cpp +7 -0
- package/libdatahike/src/datahike/impl/LibDatahikeBase.java +203 -0
- package/libdatahike/src/datahike/impl/libdatahike.clj +59 -0
- package/libdatahike/src/test_cpp.cpp +61 -0
- package/npm-package/PUBLISHING.md +140 -0
- package/npm-package/README.md +226 -0
- package/npm-package/package.template.json +34 -0
- package/npm-package/test-isomorphic.ts +281 -0
- package/npm-package/test.js +557 -0
- package/npm-package/typescript-test.ts +70 -0
- package/package.json +16 -0
- package/pydatahike/README.md +569 -0
- package/pydatahike/pyproject.toml +91 -0
- package/pydatahike/setup.py +42 -0
- package/pydatahike/src/datahike/__init__.py +134 -0
- package/pydatahike/src/datahike/_native.py +250 -0
- package/pydatahike/src/datahike/_version.py +2 -0
- package/pydatahike/src/datahike/database.py +722 -0
- package/pydatahike/src/datahike/edn.py +311 -0
- package/pydatahike/src/datahike/py.typed +0 -0
- package/pydatahike/tests/conftest.py +17 -0
- package/pydatahike/tests/test_basic.py +170 -0
- package/pydatahike/tests/test_database.py +51 -0
- package/pydatahike/tests/test_edn_conversion.py +299 -0
- package/pydatahike/tests/test_query.py +99 -0
- package/pydatahike/tests/test_schema.py +55 -0
- package/resources/clj-kondo.exports/io.replikativ/datahike/config.edn +5 -0
- package/resources/example_server.edn +4 -0
- package/shadow-cljs.edn +56 -0
- package/src/data_readers.clj +7 -0
- package/src/datahike/api/impl.cljc +176 -0
- package/src/datahike/api/specification.cljc +633 -0
- package/src/datahike/api/types.cljc +261 -0
- package/src/datahike/api.cljc +41 -0
- package/src/datahike/array.cljc +99 -0
- package/src/datahike/cli.clj +166 -0
- package/src/datahike/cljs.cljs +6 -0
- package/src/datahike/codegen/cli.clj +406 -0
- package/src/datahike/codegen/clj_kondo.clj +291 -0
- package/src/datahike/codegen/java.clj +403 -0
- package/src/datahike/codegen/naming.cljc +33 -0
- package/src/datahike/codegen/native.clj +559 -0
- package/src/datahike/codegen/pod.clj +488 -0
- package/src/datahike/codegen/python.clj +838 -0
- package/src/datahike/codegen/report.clj +55 -0
- package/src/datahike/codegen/typescript.clj +262 -0
- package/src/datahike/codegen/validation.clj +145 -0
- package/src/datahike/config.cljc +294 -0
- package/src/datahike/connections.cljc +16 -0
- package/src/datahike/connector.cljc +265 -0
- package/src/datahike/constants.cljc +142 -0
- package/src/datahike/core.cljc +297 -0
- package/src/datahike/datom.cljc +459 -0
- package/src/datahike/db/interface.cljc +119 -0
- package/src/datahike/db/search.cljc +305 -0
- package/src/datahike/db/transaction.cljc +937 -0
- package/src/datahike/db/utils.cljc +338 -0
- package/src/datahike/db.cljc +956 -0
- package/src/datahike/experimental/unstructured.cljc +126 -0
- package/src/datahike/experimental/versioning.cljc +172 -0
- package/src/datahike/externs.js +31 -0
- package/src/datahike/gc.cljc +69 -0
- package/src/datahike/http/client.clj +188 -0
- package/src/datahike/http/writer.clj +79 -0
- package/src/datahike/impl/entity.cljc +218 -0
- package/src/datahike/index/interface.cljc +93 -0
- package/src/datahike/index/persistent_set.cljc +469 -0
- package/src/datahike/index/utils.cljc +44 -0
- package/src/datahike/index.cljc +32 -0
- package/src/datahike/js/api.cljs +172 -0
- package/src/datahike/js/api_macros.clj +22 -0
- package/src/datahike/js.cljs +163 -0
- package/src/datahike/json.cljc +209 -0
- package/src/datahike/lru.cljc +146 -0
- package/src/datahike/migrate.clj +39 -0
- package/src/datahike/norm/norm.clj +245 -0
- package/src/datahike/online_gc.cljc +252 -0
- package/src/datahike/pod.clj +155 -0
- package/src/datahike/pull_api.cljc +325 -0
- package/src/datahike/query.cljc +1945 -0
- package/src/datahike/query_stats.cljc +88 -0
- package/src/datahike/readers.cljc +62 -0
- package/src/datahike/remote.cljc +218 -0
- package/src/datahike/schema.cljc +228 -0
- package/src/datahike/schema_cache.cljc +42 -0
- package/src/datahike/spec.cljc +101 -0
- package/src/datahike/store.cljc +80 -0
- package/src/datahike/tools.cljc +308 -0
- package/src/datahike/transit.cljc +80 -0
- package/src/datahike/writer.cljc +239 -0
- package/src/datahike/writing.cljc +362 -0
- package/src/deps.cljs +1 -0
- package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/insert.cljc +76 -0
- package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/upsert.cljc +128 -0
- package/src-hitchhiker-tree/datahike/index/hitchhiker_tree.cljc +213 -0
- package/test/datahike/backward_compatibility_test/src/backward_test.clj +37 -0
- package/test/datahike/integration_test/config_record_file_test.clj +14 -0
- package/test/datahike/integration_test/config_record_test.clj +14 -0
- package/test/datahike/integration_test/depr_config_uri_test.clj +15 -0
- package/test/datahike/integration_test/return_map_test.clj +62 -0
- package/test/datahike/integration_test.cljc +67 -0
- package/test/datahike/norm/norm_test.clj +124 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/003-tx-fn-test.edn +1 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/004-tx-data-and-tx-fn-test.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/01-transact-basic-characters.edn +2 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/02 add occupation.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/checksums.edn +12 -0
- package/test/datahike/norm/resources/simple-test/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/simple-test/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/simple-test/checksums.edn +4 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/003-tx-fn-test.edn +1 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/checksums.edn +6 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/004-tx-data-and-tx-fn-test.edn +5 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/checksums.edn +2 -0
- package/test/datahike/norm/resources/tx-fn-test/first/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/tx-fn-test/first/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/tx-fn-test/first/checksums.edn +4 -0
- package/test/datahike/norm/resources/tx-fn-test/second/003-tx-fn-test.edn +1 -0
- package/test/datahike/norm/resources/tx-fn-test/second/checksums.edn +2 -0
- package/test/datahike/test/api_test.cljc +895 -0
- package/test/datahike/test/array_test.cljc +40 -0
- package/test/datahike/test/attribute_refs/datoms_test.cljc +140 -0
- package/test/datahike/test/attribute_refs/db_test.cljc +42 -0
- package/test/datahike/test/attribute_refs/differences_test.cljc +515 -0
- package/test/datahike/test/attribute_refs/entity_test.cljc +89 -0
- package/test/datahike/test/attribute_refs/pull_api_test.cljc +320 -0
- package/test/datahike/test/attribute_refs/query_find_specs_test.cljc +59 -0
- package/test/datahike/test/attribute_refs/query_fns_test.cljc +130 -0
- package/test/datahike/test/attribute_refs/query_interop_test.cljc +47 -0
- package/test/datahike/test/attribute_refs/query_not_test.cljc +193 -0
- package/test/datahike/test/attribute_refs/query_or_test.cljc +137 -0
- package/test/datahike/test/attribute_refs/query_pull_test.cljc +156 -0
- package/test/datahike/test/attribute_refs/query_rules_test.cljc +176 -0
- package/test/datahike/test/attribute_refs/query_test.cljc +241 -0
- package/test/datahike/test/attribute_refs/temporal_search.cljc +22 -0
- package/test/datahike/test/attribute_refs/transact_test.cljc +220 -0
- package/test/datahike/test/attribute_refs/utils.cljc +128 -0
- package/test/datahike/test/cache_test.cljc +38 -0
- package/test/datahike/test/components_test.cljc +92 -0
- package/test/datahike/test/config_test.cljc +158 -0
- package/test/datahike/test/core_test.cljc +105 -0
- package/test/datahike/test/datom_test.cljc +44 -0
- package/test/datahike/test/db_test.cljc +54 -0
- package/test/datahike/test/entity_spec_test.cljc +159 -0
- package/test/datahike/test/entity_test.cljc +103 -0
- package/test/datahike/test/explode_test.cljc +143 -0
- package/test/datahike/test/filter_test.cljc +75 -0
- package/test/datahike/test/gc_test.cljc +159 -0
- package/test/datahike/test/http/server_test.clj +192 -0
- package/test/datahike/test/http/writer_test.clj +86 -0
- package/test/datahike/test/ident_test.cljc +32 -0
- package/test/datahike/test/index_test.cljc +345 -0
- package/test/datahike/test/insert.cljc +125 -0
- package/test/datahike/test/java_bindings_test.clj +6 -0
- package/test/datahike/test/listen_test.cljc +41 -0
- package/test/datahike/test/lookup_refs_test.cljc +266 -0
- package/test/datahike/test/lru_test.cljc +27 -0
- package/test/datahike/test/migrate_test.clj +297 -0
- package/test/datahike/test/model/core.cljc +376 -0
- package/test/datahike/test/model/invariant.cljc +142 -0
- package/test/datahike/test/model/rng.cljc +82 -0
- package/test/datahike/test/model_test.clj +217 -0
- package/test/datahike/test/nodejs_test.cljs +262 -0
- package/test/datahike/test/online_gc_test.cljc +475 -0
- package/test/datahike/test/pod_test.clj +369 -0
- package/test/datahike/test/pull_api_test.cljc +474 -0
- package/test/datahike/test/purge_test.cljc +144 -0
- package/test/datahike/test/query_aggregates_test.cljc +101 -0
- package/test/datahike/test/query_find_specs_test.cljc +52 -0
- package/test/datahike/test/query_fns_test.cljc +523 -0
- package/test/datahike/test/query_interop_test.cljc +47 -0
- package/test/datahike/test/query_not_test.cljc +189 -0
- package/test/datahike/test/query_or_test.cljc +158 -0
- package/test/datahike/test/query_pull_test.cljc +147 -0
- package/test/datahike/test/query_rules_test.cljc +248 -0
- package/test/datahike/test/query_stats_test.cljc +218 -0
- package/test/datahike/test/query_test.cljc +984 -0
- package/test/datahike/test/schema_test.cljc +424 -0
- package/test/datahike/test/specification_test.cljc +30 -0
- package/test/datahike/test/store_test.cljc +78 -0
- package/test/datahike/test/stress_test.cljc +57 -0
- package/test/datahike/test/time_variance_test.cljc +518 -0
- package/test/datahike/test/tools_test.clj +134 -0
- package/test/datahike/test/transact_test.cljc +518 -0
- package/test/datahike/test/tuples_test.cljc +564 -0
- package/test/datahike/test/unstructured_test.cljc +291 -0
- package/test/datahike/test/upsert_impl_test.cljc +205 -0
- package/test/datahike/test/upsert_test.cljc +363 -0
- package/test/datahike/test/utils.cljc +110 -0
- package/test/datahike/test/validation_test.cljc +48 -0
- package/test/datahike/test/versioning_test.cljc +56 -0
- package/test/datahike/test.cljc +66 -0
- package/tests.edn +24 -0
|
@@ -0,0 +1,937 @@
|
|
|
1
|
+
(ns datahike.db.transaction
|
|
2
|
+
(:require
|
|
3
|
+
[clojure.spec.alpha :as s]
|
|
4
|
+
[clojure.string :as str]
|
|
5
|
+
[datahike.index :as di]
|
|
6
|
+
[datahike.datom :as dd :refer [datom datom-tx datom-added datom?]]
|
|
7
|
+
#?(:cljs [datahike.db :refer [HistoricalDB]])
|
|
8
|
+
[datahike.db.interface :as dbi]
|
|
9
|
+
[datahike.db.search :as dbs]
|
|
10
|
+
[datahike.db.utils :as dbu]
|
|
11
|
+
[datahike.constants :refer [tx0]]
|
|
12
|
+
[datahike.tools :refer [get-date raise]]
|
|
13
|
+
[datahike.schema :as ds]
|
|
14
|
+
[org.replikativ.persistent-sorted-set.arrays :as arrays])
|
|
15
|
+
#?(:cljs (:require-macros [datahike.datom :refer [datom]]
|
|
16
|
+
[datahike.tools :refer [raise]]))
|
|
17
|
+
#?(:clj (:import [clojure.lang ExceptionInfo]
|
|
18
|
+
[datahike.datom Datom]
|
|
19
|
+
[datahike.db HistoricalDB]
|
|
20
|
+
[java.util Date])))
|
|
21
|
+
|
|
22
|
+
(defn validate-datom [db ^Datom datom]
|
|
23
|
+
(when (and (datom-added datom)
|
|
24
|
+
(dbu/is-attr? db (.-a datom) :db/unique))
|
|
25
|
+
(when-let [found (not-empty (dbi/datoms db :avet [(.-a datom) (.-v datom)]))]
|
|
26
|
+
(raise "Cannot add " datom " because of unique constraint: " found
|
|
27
|
+
{:error :transact/unique :attribute (.-a datom) :datom datom}))))
|
|
28
|
+
|
|
29
|
+
(defn- validate-val [v [_ _ a _ _ :as at] {:keys [config schema ref-ident-map] :as db}]
|
|
30
|
+
(when (nil? v)
|
|
31
|
+
(raise "Cannot store nil as a value at " at
|
|
32
|
+
{:error :transact/syntax, :value v, :context at}))
|
|
33
|
+
(let [{:keys [attribute-refs? schema-flexibility]} config
|
|
34
|
+
a-ident (if (and attribute-refs? (number? a)) (dbi/-ident-for db a) a)
|
|
35
|
+
v-ident (if (and attribute-refs?
|
|
36
|
+
(contains? (dbi/-system-entities db) a)
|
|
37
|
+
(not (nil? (ref-ident-map v))))
|
|
38
|
+
(ref-ident-map v)
|
|
39
|
+
v)]
|
|
40
|
+
|
|
41
|
+
(when (= :write schema-flexibility)
|
|
42
|
+
(let [schema-spec (if (or (ds/meta-attr? a-ident) (ds/schema-attr? a-ident))
|
|
43
|
+
ds/implicit-schema-spec
|
|
44
|
+
schema)]
|
|
45
|
+
(when-not (ds/value-valid? a-ident v-ident schema)
|
|
46
|
+
(raise "Bad entity value " v-ident " at " at ", value does not match schema definition. Must be conform to: "
|
|
47
|
+
(ds/describe-type (get-in schema-spec [a-ident :db/valueType]))
|
|
48
|
+
{:error :transact/schema :value v-ident :attribute a-ident :schema (get-in db [:schema a-ident])}))))))
|
|
49
|
+
|
|
50
|
+
(defn- current-tx [report]
|
|
51
|
+
(inc (get-in report [:db-before :max-tx])))
|
|
52
|
+
|
|
53
|
+
(defn next-eid [db]
|
|
54
|
+
(inc (:max-eid db)))
|
|
55
|
+
|
|
56
|
+
(defn- #?@(:clj [^Boolean tx-id?]
|
|
57
|
+
:cljs [^boolean tx-id?])
|
|
58
|
+
[e]
|
|
59
|
+
(or (= e :db/current-tx)
|
|
60
|
+
(= e ":db/current-tx") ;; for datahike.js interop
|
|
61
|
+
(= e "datomic.tx")
|
|
62
|
+
(= e "datahike.tx")))
|
|
63
|
+
|
|
64
|
+
(defn- #?@(:clj [^Boolean tempid?]
|
|
65
|
+
:cljs [^boolean tempid?])
|
|
66
|
+
[x]
|
|
67
|
+
(or (and (number? x) (neg? x)) (string? x)))
|
|
68
|
+
|
|
69
|
+
(defn advance-max-eid [db eid]
|
|
70
|
+
(cond-> db
|
|
71
|
+
(and (> eid (:max-eid db))
|
|
72
|
+
(< eid tx0)) ;; do not trigger advance if transaction id was referenced
|
|
73
|
+
(assoc :max-eid eid)))
|
|
74
|
+
|
|
75
|
+
(defn- allocate-eid
|
|
76
|
+
([report eid]
|
|
77
|
+
(update-in report [:db-after] advance-max-eid eid))
|
|
78
|
+
([report e eid]
|
|
79
|
+
(cond-> report
|
|
80
|
+
(tx-id? e)
|
|
81
|
+
(assoc-in [:tempids e] eid)
|
|
82
|
+
(tempid? e)
|
|
83
|
+
(assoc-in [:tempids e] eid)
|
|
84
|
+
true
|
|
85
|
+
(update-in [:db-after] advance-max-eid eid))))
|
|
86
|
+
|
|
87
|
+
(defn update-schema [db ^Datom datom]
|
|
88
|
+
(let [schema (dbi/-schema db)
|
|
89
|
+
attribute-refs? (:attribute-refs? (dbi/-config db))
|
|
90
|
+
e (.-e datom)
|
|
91
|
+
a (.-a datom)
|
|
92
|
+
v (.-v datom)
|
|
93
|
+
a-ident (if attribute-refs? (dbi/-ident-for db a) a)
|
|
94
|
+
v-ident (if (and attribute-refs? (contains? (dbi/-system-entities db) v))
|
|
95
|
+
(dbi/-ident-for db v)
|
|
96
|
+
v)]
|
|
97
|
+
(when (and attribute-refs? (contains? (dbi/-system-entities db) e))
|
|
98
|
+
(raise "System schema entity cannot be changed"
|
|
99
|
+
{:error :transact/schema :entity-id e}))
|
|
100
|
+
(if (= a-ident :db/ident)
|
|
101
|
+
(if (schema v-ident)
|
|
102
|
+
(raise (str "Schema with attribute " v-ident " already exists")
|
|
103
|
+
{:error :transact/schema :attribute v-ident})
|
|
104
|
+
(-> (assoc-in db [:schema v-ident] (merge (or (schema e) {}) (hash-map a-ident v-ident)))
|
|
105
|
+
(assoc-in [:schema e] v-ident)
|
|
106
|
+
(assoc-in [:ident-ref-map v-ident] e)
|
|
107
|
+
(assoc-in [:ref-ident-map e] v-ident)))
|
|
108
|
+
(if-let [schema-entry (schema e)]
|
|
109
|
+
(if (schema schema-entry)
|
|
110
|
+
(update-in db [:schema schema-entry a-ident] (fn [old]
|
|
111
|
+
(if (ds/entity-spec-attr? a-ident)
|
|
112
|
+
(if old
|
|
113
|
+
(conj old v-ident)
|
|
114
|
+
[v-ident])
|
|
115
|
+
v-ident)))
|
|
116
|
+
(assoc-in db [:schema e a-ident] v-ident))
|
|
117
|
+
(assoc-in db [:schema e] (hash-map a-ident v-ident))))))
|
|
118
|
+
|
|
119
|
+
(defn update-rschema [db]
|
|
120
|
+
(assoc db :rschema (dbu/rschema (:schema db))))
|
|
121
|
+
|
|
122
|
+
(defn remove-schema [db ^Datom datom]
|
|
123
|
+
(let [schema (dbi/-schema db)
|
|
124
|
+
attribute-refs? (:attribute-refs? (dbi/-config db))
|
|
125
|
+
e (.-e datom)
|
|
126
|
+
a (.-a datom)
|
|
127
|
+
v (.-v datom)
|
|
128
|
+
a-ident (if attribute-refs? (dbi/-ident-for db a) a)
|
|
129
|
+
v-ident (if (and attribute-refs? (contains? (dbi/-system-entities db) v))
|
|
130
|
+
(dbi/-ident-for db v)
|
|
131
|
+
v)]
|
|
132
|
+
(when (and attribute-refs? (contains? (dbi/-system-entities db) e))
|
|
133
|
+
(raise "System schema entity cannot be changed"
|
|
134
|
+
{:error :retract/schema :entity-id e}))
|
|
135
|
+
(if (= a-ident :db/ident)
|
|
136
|
+
(if-not (schema v-ident)
|
|
137
|
+
(let [err-msg (str "Schema with attribute " v-ident " does not exist")
|
|
138
|
+
err-map {:error :retract/schema :attribute v-ident}]
|
|
139
|
+
(throw (ex-info err-msg err-map)))
|
|
140
|
+
(-> (assoc-in db [:schema e] (dissoc (schema v-ident) a-ident))
|
|
141
|
+
(update-in [:schema] #(dissoc % v-ident))
|
|
142
|
+
(update-in [:ident-ref-map] #(dissoc % v-ident))
|
|
143
|
+
(update-in [:ref-ident-map] #(dissoc % e))))
|
|
144
|
+
(if-let [schema-entry (schema e)]
|
|
145
|
+
(if (schema schema-entry)
|
|
146
|
+
(update-in db [:schema schema-entry] #(dissoc % a-ident))
|
|
147
|
+
(update-in db [:schema e] #(dissoc % a-ident v-ident)))
|
|
148
|
+
(let [err-msg (str "Schema with entity id " e " does not exist")
|
|
149
|
+
err-map {:error :retract/schema :entity-id e :attribute a :value e}]
|
|
150
|
+
(throw (ex-info err-msg err-map)))))))
|
|
151
|
+
|
|
152
|
+
;; In context of `with-datom` we can use faster comparators which
|
|
153
|
+
;; do not check for nil (~10-15% performance gain in `transact`)
|
|
154
|
+
|
|
155
|
+
(defn- with-datom [db ^Datom datom]
|
|
156
|
+
(validate-datom db datom)
|
|
157
|
+
(let [{a-ident :ident} (dbu/attr-info db (.-a datom))
|
|
158
|
+
indexing? (dbu/indexing? db a-ident)
|
|
159
|
+
schema? (or (ds/schema-attr? a-ident) (ds/entity-spec-attr? a-ident))
|
|
160
|
+
keep-history? (and (dbi/-keep-history? db) (not (dbu/no-history? db a-ident)))
|
|
161
|
+
op-count (:op-count db)]
|
|
162
|
+
(if (datom-added datom)
|
|
163
|
+
(cond-> db
|
|
164
|
+
true (update-in [:eavt] #(di/-insert % datom :eavt op-count))
|
|
165
|
+
true (update-in [:aevt] #(di/-insert % datom :aevt op-count))
|
|
166
|
+
indexing? (update-in [:avet] #(di/-insert % datom :avet op-count))
|
|
167
|
+
true (advance-max-eid (.-e datom))
|
|
168
|
+
true (update :hash + (hash datom))
|
|
169
|
+
schema? (-> (update-schema datom)
|
|
170
|
+
update-rschema)
|
|
171
|
+
true (update :op-count inc))
|
|
172
|
+
|
|
173
|
+
(if-some [removing ^Datom (first (dbi/search db [(.-e datom) (.-a datom) (.-v datom)]))]
|
|
174
|
+
(cond-> db
|
|
175
|
+
true (update-in [:eavt] #(di/-remove % removing :eavt op-count))
|
|
176
|
+
true (update-in [:aevt] #(di/-remove % removing :aevt op-count))
|
|
177
|
+
indexing? (update-in [:avet] #(di/-remove % removing :avet op-count))
|
|
178
|
+
true (update :hash - (hash removing))
|
|
179
|
+
schema? (-> (remove-schema datom) update-rschema)
|
|
180
|
+
keep-history? (update-in [:temporal-eavt] #(di/-temporal-insert % removing :eavt op-count))
|
|
181
|
+
keep-history? (update-in [:temporal-eavt] #(di/-temporal-insert % datom :eavt (inc op-count)))
|
|
182
|
+
keep-history? (update-in [:temporal-aevt] #(di/-temporal-insert % removing :aevt op-count))
|
|
183
|
+
keep-history? (update-in [:temporal-aevt] #(di/-temporal-insert % datom :aevt (inc op-count)))
|
|
184
|
+
keep-history? (update :hash + (hash datom))
|
|
185
|
+
(and keep-history? indexing?) (update-in [:temporal-avet] #(di/-temporal-insert % removing :avet op-count))
|
|
186
|
+
(and keep-history? indexing?) (update-in [:temporal-avet] #(di/-temporal-insert % datom :avet (inc op-count)))
|
|
187
|
+
true (update :op-count + (if (or keep-history? indexing?) 2 1)))
|
|
188
|
+
db))))
|
|
189
|
+
|
|
190
|
+
(defn- with-temporal-datom [db ^Datom datom]
|
|
191
|
+
(let [{a-ident :ident} (dbu/attr-info db (.-a datom))
|
|
192
|
+
indexing? (dbu/indexing? db a-ident)
|
|
193
|
+
schema? (ds/schema-attr? a-ident)
|
|
194
|
+
current-datom ^Datom (first (dbi/search db [(.-e datom) (.-a datom) (.-v datom)]))
|
|
195
|
+
history-datom ^Datom (first (dbs/search-temporal-indices db [(.-e datom) (.-a datom) (.-v datom) (.-tx datom)]))
|
|
196
|
+
current? (not (nil? current-datom))
|
|
197
|
+
history? (not (nil? history-datom))
|
|
198
|
+
op-count (:op-count db)]
|
|
199
|
+
(cond-> db
|
|
200
|
+
current? (update-in [:eavt] #(di/-remove % current-datom :eavt op-count))
|
|
201
|
+
current? (update-in [:aevt] #(di/-remove % current-datom :aevt op-count))
|
|
202
|
+
(and current? indexing?) (update-in [:avet] #(di/-remove % current-datom :avet op-count))
|
|
203
|
+
current? (update :hash - (hash current-datom))
|
|
204
|
+
(and current? schema?) (-> (remove-schema datom) update-rschema)
|
|
205
|
+
history? (update-in [:temporal-eavt] #(di/-remove % history-datom :eavt op-count))
|
|
206
|
+
history? (update-in [:temporal-aevt] #(di/-remove % history-datom :aevt op-count))
|
|
207
|
+
(and history? indexing?) (update-in [:temporal-avet] #(di/-remove % history-datom :avet op-count))
|
|
208
|
+
(or current? history?) (update :op-count inc))))
|
|
209
|
+
|
|
210
|
+
(defn- queue-tuple [queue tuple idx db e v]
|
|
211
|
+
(let [tuple-value (or (get queue tuple)
|
|
212
|
+
(:v (first (dbi/datoms db :eavt [e tuple])))
|
|
213
|
+
(vec (repeat (-> db (dbi/-schema) (get tuple) :db/tupleAttrs count) nil)))
|
|
214
|
+
tuple-value' (assoc tuple-value idx v)]
|
|
215
|
+
(assoc queue tuple tuple-value')))
|
|
216
|
+
|
|
217
|
+
(defn- queue-tuples
|
|
218
|
+
"Assuming the attribute we are concerned with is :a and its associated value is 'a',
|
|
219
|
+
returns {:a+b+c [a nil nil], :a+d [a, nil]}"
|
|
220
|
+
[queue tuples db e v]
|
|
221
|
+
(reduce-kv
|
|
222
|
+
(fn [queue tuple idx]
|
|
223
|
+
(queue-tuple queue tuple idx db e v))
|
|
224
|
+
queue
|
|
225
|
+
tuples))
|
|
226
|
+
|
|
227
|
+
(defn validate-datom-upsert [db ^Datom datom]
|
|
228
|
+
(when (dbu/is-attr? db (.-a datom) :db/unique)
|
|
229
|
+
(when-let [old (first (dbi/datoms db :avet [(.-a datom) (.-v datom)]))]
|
|
230
|
+
(when-not (= (.-e datom) (.-e ^Datom old))
|
|
231
|
+
(raise "Cannot add " datom " because of unique constraint: " old
|
|
232
|
+
{:error :transact/unique
|
|
233
|
+
:attribute (.-a datom)
|
|
234
|
+
:datom datom})))))
|
|
235
|
+
|
|
236
|
+
(defn- with-datom-upsert [db ^Datom datom]
|
|
237
|
+
(validate-datom-upsert db datom)
|
|
238
|
+
(let [indexing? (dbu/indexing? db (.-a datom))
|
|
239
|
+
{a-ident :ident} (dbu/attr-info db (.-a datom))
|
|
240
|
+
schema? (ds/schema-attr? a-ident)
|
|
241
|
+
keep-history? (and (dbi/-keep-history? db) (not (dbu/no-history? db a-ident))
|
|
242
|
+
(not= :db/txInstant a-ident))
|
|
243
|
+
op-count (:op-count db)
|
|
244
|
+
old-datom (first (di/-slice (:eavt db)
|
|
245
|
+
(dd/datom (.-e datom) (.-a datom) nil (.-tx datom))
|
|
246
|
+
(dd/datom (.-e datom) (.-a datom) nil (.-tx datom))
|
|
247
|
+
:eavt))]
|
|
248
|
+
(cond-> db
|
|
249
|
+
;; Optimistic removal of the schema entry (because we don't know whether it is already present or not)
|
|
250
|
+
schema? (try
|
|
251
|
+
(-> db (remove-schema datom) update-rschema)
|
|
252
|
+
(catch ExceptionInfo _e
|
|
253
|
+
db))
|
|
254
|
+
|
|
255
|
+
keep-history? (update-in [:temporal-eavt] #(di/-temporal-upsert % datom :eavt op-count old-datom))
|
|
256
|
+
true (update-in [:eavt] #(di/-upsert % datom :eavt op-count old-datom))
|
|
257
|
+
|
|
258
|
+
keep-history? (update-in [:temporal-aevt] #(di/-temporal-upsert % datom :aevt op-count old-datom))
|
|
259
|
+
true (update-in [:aevt] #(di/-upsert % datom :aevt op-count old-datom))
|
|
260
|
+
|
|
261
|
+
(and keep-history? indexing?) (update-in [:temporal-avet] #(di/-temporal-upsert % datom :avet op-count old-datom))
|
|
262
|
+
indexing? (update-in [:avet] #(di/-upsert % datom :avet op-count old-datom))
|
|
263
|
+
|
|
264
|
+
true (update :op-count inc)
|
|
265
|
+
true (advance-max-eid (.-e datom))
|
|
266
|
+
true (update :hash + (hash datom))
|
|
267
|
+
schema? (-> (update-schema datom) update-rschema))))
|
|
268
|
+
|
|
269
|
+
(defn- transact-report
|
|
270
|
+
([report datom] (transact-report report datom false))
|
|
271
|
+
([report datom upsert?]
|
|
272
|
+
(let [db (:db-after report)
|
|
273
|
+
a (:a datom)
|
|
274
|
+
update-fn (if upsert? with-datom-upsert with-datom)
|
|
275
|
+
report' (-> report
|
|
276
|
+
(update-in [:db-after] update-fn datom)
|
|
277
|
+
(update-in [:tx-data] conj datom))]
|
|
278
|
+
(if (dbu/tuple-source? db a)
|
|
279
|
+
(let [e (:e datom)
|
|
280
|
+
v (if (datom-added datom) (:v datom) nil)
|
|
281
|
+
queue (or (-> report' ::queued-tuples (get e)) {})
|
|
282
|
+
tuples (get (dbi/-attrs-by db :db/attrTuples) a)
|
|
283
|
+
queue' (queue-tuples queue tuples db e v)]
|
|
284
|
+
(update report' ::queued-tuples assoc e queue'))
|
|
285
|
+
report'))))
|
|
286
|
+
|
|
287
|
+
(defn- check-upsert-conflict [entity acc]
|
|
288
|
+
(let [[e a v] acc
|
|
289
|
+
_e (:db/id entity)]
|
|
290
|
+
(if (or (nil? _e)
|
|
291
|
+
(tempid? _e)
|
|
292
|
+
(nil? acc)
|
|
293
|
+
(== _e e))
|
|
294
|
+
acc
|
|
295
|
+
(raise "Conflicting upsert: " [a v] " resolves to " e
|
|
296
|
+
", but entity already has :db/id " _e
|
|
297
|
+
{:error :transact/upsert
|
|
298
|
+
:entity entity
|
|
299
|
+
:assertion acc}))))
|
|
300
|
+
|
|
301
|
+
(defn- upsert-eid [db entity tempids] ;; TODO: adjust to datascript?
|
|
302
|
+
(when-let [unique-idents (not-empty (dbi/-attrs-by db :db.unique/identity))]
|
|
303
|
+
(let [unique-tuple-idents (clojure.set/intersection
|
|
304
|
+
(dbi/-attrs-by db :db.type/tuple)
|
|
305
|
+
unique-idents)
|
|
306
|
+
found-eav
|
|
307
|
+
(reduce-kv
|
|
308
|
+
(fn [acc a-ident v-original] ;; acc = [e a v]
|
|
309
|
+
(if-not (contains? unique-idents a-ident)
|
|
310
|
+
acc
|
|
311
|
+
(let [a (if (:attribute-refs? (dbi/-config db))
|
|
312
|
+
(dbi/-ref-for db a-ident)
|
|
313
|
+
a-ident)
|
|
314
|
+
tempid-val (and (dbu/ref? db a-ident) (tempid? v-original))
|
|
315
|
+
v (if tempid-val
|
|
316
|
+
(tempids v-original)
|
|
317
|
+
v-original)]
|
|
318
|
+
(if-some [e (when v
|
|
319
|
+
(validate-val v [nil nil a v nil] db)
|
|
320
|
+
(:e (first (dbi/datoms db :avet [a v]))))]
|
|
321
|
+
(cond
|
|
322
|
+
(nil? acc) [e a v] ;; first upsert
|
|
323
|
+
(= (get acc 0) e) acc ;; second+ upsert, but does not conflict
|
|
324
|
+
:else
|
|
325
|
+
(let [[_e _a _v] acc]
|
|
326
|
+
(raise "Conflicting upserts: " [_a _v] " resolves to " _e
|
|
327
|
+
", but " [a v] " resolves to " e
|
|
328
|
+
{:error :transact/upsert
|
|
329
|
+
:entity entity
|
|
330
|
+
:assertion [e a v]
|
|
331
|
+
:conflict [_e _a _v]})))
|
|
332
|
+
acc)))) ;; upsert attr, but resolves to nothing ;; non-upsert attr
|
|
333
|
+
nil
|
|
334
|
+
entity)
|
|
335
|
+
|
|
336
|
+
found-eav-including-composite-tuples
|
|
337
|
+
(reduce
|
|
338
|
+
(fn [acc a-tuple]
|
|
339
|
+
(let [tuple-attrs (get-in (dbi/-schema db) [a-tuple :db/tupleAttrs])
|
|
340
|
+
contains-tuple-attrs? (clojure.set/subset?
|
|
341
|
+
(set tuple-attrs)
|
|
342
|
+
(set (keys entity)))
|
|
343
|
+
tuple-contains-tempids? (and contains-tuple-attrs?
|
|
344
|
+
(some (fn [a] (and (dbu/ref? db a)
|
|
345
|
+
(tempid? (get entity a))))
|
|
346
|
+
tuple-attrs))
|
|
347
|
+
v-tuple (and contains-tuple-attrs?
|
|
348
|
+
(not tuple-contains-tempids?)
|
|
349
|
+
(mapv (fn [a]
|
|
350
|
+
(let [v (get entity a)]
|
|
351
|
+
(validate-val v [nil nil a v nil] db)
|
|
352
|
+
(if (dbu/ref? db a)
|
|
353
|
+
(dbu/entid-strict db v)
|
|
354
|
+
v)))
|
|
355
|
+
tuple-attrs))]
|
|
356
|
+
(if-let [e (and contains-tuple-attrs?
|
|
357
|
+
(not tuple-contains-tempids?)
|
|
358
|
+
(:e (first (dbi/datoms db :avet [a-tuple v-tuple]))))]
|
|
359
|
+
(cond
|
|
360
|
+
(nil? acc) [e a-tuple v-tuple] ;; first upsert
|
|
361
|
+
(= (get acc 0) e) acc ;; second+ upsert, but does not conflict
|
|
362
|
+
:else
|
|
363
|
+
(let [[_e _a _v] acc]
|
|
364
|
+
(raise "Conflicting upserts: " [_a _v] " resolves to " _e
|
|
365
|
+
", but " [a-tuple v-tuple] " resolves to " e
|
|
366
|
+
{:error :transact/upsert
|
|
367
|
+
:entity entity
|
|
368
|
+
:assertion [e a-tuple v-tuple]
|
|
369
|
+
:conflict [_e _a _v]})))
|
|
370
|
+
acc))) ;; upsert attr, but resolves to nothing ;; non-upsert attr
|
|
371
|
+
found-eav
|
|
372
|
+
unique-tuple-idents)]
|
|
373
|
+
(->> found-eav-including-composite-tuples
|
|
374
|
+
(check-upsert-conflict entity)
|
|
375
|
+
first)))) ;; getting eid from acc
|
|
376
|
+
|
|
377
|
+
;; multivals/reverse can be specified as coll or as a single value, trying to guess
|
|
378
|
+
(defn- maybe-wrap-multival [db a-ident vs]
|
|
379
|
+
(cond
|
|
380
|
+
;; not a multival context
|
|
381
|
+
(not (or (dbu/reverse-ref? a-ident)
|
|
382
|
+
(dbu/multival? db a-ident)))
|
|
383
|
+
[vs]
|
|
384
|
+
|
|
385
|
+
;; not a collection at all, so definitely a single value
|
|
386
|
+
(not (or (arrays/array? vs)
|
|
387
|
+
(and (coll? vs) (not (map? vs)))))
|
|
388
|
+
[vs]
|
|
389
|
+
|
|
390
|
+
;; probably lookup ref, but not an entity spec
|
|
391
|
+
(and (= (count vs) 2)
|
|
392
|
+
(keyword? (first vs))
|
|
393
|
+
(dbu/is-attr? db (first vs) :db.unique/identity)
|
|
394
|
+
(not (ds/entity-spec-attr? a-ident)))
|
|
395
|
+
[vs]
|
|
396
|
+
|
|
397
|
+
:else vs))
|
|
398
|
+
|
|
399
|
+
(defn- explode [db entity]
|
|
400
|
+
(let [eid (:db/id entity)
|
|
401
|
+
attribute-refs? (:attribute-refs? (dbi/-config db))
|
|
402
|
+
_ (when (and attribute-refs? (contains? (dbi/-system-entities db) eid))
|
|
403
|
+
(raise "Entity with ID " eid " is a system attribute " (dbi/-ident-for db eid) " and cannot be changed"
|
|
404
|
+
{:error :transact/syntax, :eid eid, :attribute (dbi/-ident-for db eid) :context entity}))
|
|
405
|
+
ensure (:db/ensure entity)
|
|
406
|
+
entities (for [[a-ident vs] entity
|
|
407
|
+
:when (not (or (= a-ident :db/id) (= a-ident :db/ensure)))
|
|
408
|
+
:let [_ (dbu/validate-attr-ident a-ident {:db/id eid, a-ident vs} db)
|
|
409
|
+
reverse? (dbu/reverse-ref? a-ident)
|
|
410
|
+
straight-a-ident (if reverse? (dbu/reverse-ref a-ident) a-ident)
|
|
411
|
+
straight-a (if attribute-refs?
|
|
412
|
+
(dbi/-ref-for db straight-a-ident) ;; translation to datom format
|
|
413
|
+
straight-a-ident)
|
|
414
|
+
_ (when (and reverse? (not (dbu/ref? db straight-a-ident)))
|
|
415
|
+
(raise "Bad attribute " a-ident ": reverse attribute name requires {:db/valueType :db.type/ref} in schema"
|
|
416
|
+
{:error :transact/syntax, :attribute a-ident, :context {:db/id eid, a-ident vs}}))]
|
|
417
|
+
v (maybe-wrap-multival db a-ident vs)]
|
|
418
|
+
(if (and (dbu/ref? db straight-a-ident) (map? v)) ;; another entity specified as nested map
|
|
419
|
+
(assoc v (dbu/reverse-ref a-ident) eid)
|
|
420
|
+
(if reverse?
|
|
421
|
+
[:db/add v straight-a eid]
|
|
422
|
+
[:db/add eid straight-a
|
|
423
|
+
(if (and attribute-refs?
|
|
424
|
+
(dbu/is-attr? db straight-a-ident :db/systemAttribRef)
|
|
425
|
+
(ds/is-system-keyword? v)) ;; translation of system enums
|
|
426
|
+
(dbi/-ref-for db v)
|
|
427
|
+
v)])))]
|
|
428
|
+
(if ensure
|
|
429
|
+
(let [{:keys [:db.entity/attrs :db.entity/preds]} (-> db :schema ensure)]
|
|
430
|
+
(if (empty? attrs)
|
|
431
|
+
(if (empty? preds)
|
|
432
|
+
entities
|
|
433
|
+
(concat entities [[:db.ensure/preds eid ensure preds]]))
|
|
434
|
+
(if (empty? preds)
|
|
435
|
+
(concat entities [[:db.ensure/attrs eid ensure attrs]])
|
|
436
|
+
(concat entities [[:db.ensure/attrs eid ensure attrs]
|
|
437
|
+
[:db.ensure/preds eid ensure preds]]))))
|
|
438
|
+
entities)))
|
|
439
|
+
|
|
440
|
+
(defn- transact-add [{:keys [db-after] :as report} [_ e a v tx :as ent]]
|
|
441
|
+
(let [a (dbu/normalize-and-validate-attr a ent db-after)
|
|
442
|
+
_ (validate-val v ent db-after)
|
|
443
|
+
attribute-refs? (:attribute-refs? (dbi/-config db-after))
|
|
444
|
+
tx (or tx (current-tx report))
|
|
445
|
+
db db-after
|
|
446
|
+
e (dbu/entid-strict db e)
|
|
447
|
+
a-ident (if attribute-refs? (dbi/-ident-for db a) a)
|
|
448
|
+
v (if (dbu/ref? db a-ident) (dbu/entid-strict db v) v)
|
|
449
|
+
new-datom (datom e a v tx)
|
|
450
|
+
upsert? (not (dbu/multival? db a))]
|
|
451
|
+
(transact-report report new-datom upsert?)))
|
|
452
|
+
|
|
453
|
+
(defn- transact-retract-datom
|
|
454
|
+
([report ^Datom d] (transact-retract-datom report d false))
|
|
455
|
+
([report ^Datom d keep-tx-id]
|
|
456
|
+
(let [txid (or (and keep-tx-id (datom-tx d)) (current-tx report))]
|
|
457
|
+
(transact-report report (datom (.-e d) (.-a d) (.-v d) txid false)))))
|
|
458
|
+
|
|
459
|
+
(defn- transact-purge-datom [report ^Datom d]
|
|
460
|
+
(update-in report [:db-after] with-temporal-datom d))
|
|
461
|
+
|
|
462
|
+
(defn- retract-components [db datoms]
|
|
463
|
+
(into #{} (comp
|
|
464
|
+
(filter (fn [^Datom d] (dbu/component? db (.-a d))))
|
|
465
|
+
(map (fn [^Datom d] [:db.fn/retractEntity (.-v d)]))) datoms))
|
|
466
|
+
|
|
467
|
+
(defn- purge-components [db datoms]
|
|
468
|
+
(let [xf (comp
|
|
469
|
+
(filter (fn [^Datom d] (dbu/component? db (.-a d))))
|
|
470
|
+
(map (fn [^Datom d] [:db.purge/entity (.-v d)])))]
|
|
471
|
+
(into #{} xf datoms)))
|
|
472
|
+
|
|
473
|
+
(declare transact-tx-data)
|
|
474
|
+
|
|
475
|
+
(defn- retry-with-tempid [initial-report report es tempid upserted-eid]
|
|
476
|
+
(if (contains? (:tempids initial-report) tempid)
|
|
477
|
+
(raise "Conflicting upsert: " tempid " resolves"
|
|
478
|
+
" both to " upserted-eid " and " (get-in initial-report [:tempids tempid])
|
|
479
|
+
{:error :transact/upsert})
|
|
480
|
+
;; try to re-run from the beginning
|
|
481
|
+
;; but remembering that `tempid` will resolve to `upserted-eid`
|
|
482
|
+
(let [tempids' (-> (:tempids report)
|
|
483
|
+
(assoc tempid upserted-eid))
|
|
484
|
+
report' (assoc initial-report :tempids tempids')]
|
|
485
|
+
(transact-tx-data report' es))))
|
|
486
|
+
|
|
487
|
+
(defn assert-preds [db [_ e _ preds]]
|
|
488
|
+
#?(:cljs (throw (ex-info "tx predicate resolution is not supported in cljs at this time" {:e e :preds preds}))
|
|
489
|
+
:clj
|
|
490
|
+
(reduce
|
|
491
|
+
(fn [coll pred]
|
|
492
|
+
(if ((resolve pred) db e)
|
|
493
|
+
coll
|
|
494
|
+
(conj coll pred)))
|
|
495
|
+
#{} preds)))
|
|
496
|
+
|
|
497
|
+
(def builtin-op?
|
|
498
|
+
#{:db.fn/call
|
|
499
|
+
:db.fn/cas
|
|
500
|
+
:db/cas
|
|
501
|
+
:db/add
|
|
502
|
+
:db/retract
|
|
503
|
+
:db.fn/retractAttribute
|
|
504
|
+
:db.fn/retractEntity
|
|
505
|
+
:db/retractEntity
|
|
506
|
+
:db/purge
|
|
507
|
+
:db.ensure/attrs
|
|
508
|
+
:db.ensure/preds
|
|
509
|
+
:db.purge/entity
|
|
510
|
+
:db.purge/attribute
|
|
511
|
+
:db.history.purge/before})
|
|
512
|
+
|
|
513
|
+
(defn flush-tuples
|
|
514
|
+
"Generates all the add or retract operations needed for updating the states of composite tuples.
|
|
515
|
+
E.g., if '::queued-tuples' contains {100 {:a+b+c [123 nil nil]}}, this function creates this vector [:db/add 100 :a+b+c [123 nil nil]]"
|
|
516
|
+
[report]
|
|
517
|
+
(let [db (:db-after report)]
|
|
518
|
+
(reduce-kv
|
|
519
|
+
(fn [entities eid tuples+values]
|
|
520
|
+
(reduce-kv
|
|
521
|
+
(fn [entities tuple value]
|
|
522
|
+
(let [value (if (every? nil? value) nil value)
|
|
523
|
+
current (:v (first (dbi/datoms db :eavt [eid tuple])))]
|
|
524
|
+
(cond
|
|
525
|
+
(= value current) entities
|
|
526
|
+
;; adds ::internal to meta-data to mean that these datoms were generated internally.
|
|
527
|
+
(nil? value) (conj entities ^::internal [:db/retract eid tuple current])
|
|
528
|
+
:else (conj entities ^::internal [:db/add eid tuple value]))))
|
|
529
|
+
entities
|
|
530
|
+
tuples+values))
|
|
531
|
+
[]
|
|
532
|
+
(::queued-tuples report))))
|
|
533
|
+
|
|
534
|
+
(defn flush-tx-meta
|
|
535
|
+
"Generates add-operations for transaction meta data."
|
|
536
|
+
[{:keys [tx-meta db-before] :as report}]
|
|
537
|
+
(let [;; tx-meta (merge {:db/txInstant (get-date)} tx-meta)
|
|
538
|
+
tid (current-tx report)
|
|
539
|
+
{:keys [attribute-refs?]} (dbi/-config db-before)]
|
|
540
|
+
(reduce-kv
|
|
541
|
+
(fn [entities attribute value]
|
|
542
|
+
(let [straight-a (if attribute-refs? (dbi/-ref-for db-before attribute) attribute)]
|
|
543
|
+
(if (some? straight-a)
|
|
544
|
+
(conj entities
|
|
545
|
+
[:db/add
|
|
546
|
+
tid
|
|
547
|
+
straight-a
|
|
548
|
+
value
|
|
549
|
+
tid])
|
|
550
|
+
(raise "Bad transaction meta attribute " attribute " at " tx-meta ", not defined in system or current schema"
|
|
551
|
+
{:error :transact/schema :attribute attribute :context tx-meta}))))
|
|
552
|
+
[]
|
|
553
|
+
tx-meta)))
|
|
554
|
+
|
|
555
|
+
(defn check-schema-update [db entity new-eid]
|
|
556
|
+
(when (ds/schema-entity? entity)
|
|
557
|
+
(when (and (contains? entity :db/ident)
|
|
558
|
+
(ds/is-system-keyword? (:db/ident entity)))
|
|
559
|
+
(raise "Using namespace 'db' for attribute identifiers is not allowed"
|
|
560
|
+
{:error :transact/schema :entity entity}))
|
|
561
|
+
(if-let [attr-name (get-in db [:schema new-eid])]
|
|
562
|
+
(when-let [invalid-updates (ds/find-invalid-schema-updates entity (get-in db [:schema attr-name]))]
|
|
563
|
+
(when-not (empty? invalid-updates)
|
|
564
|
+
(raise "Update not supported for these schema attributes"
|
|
565
|
+
{:error :transact/schema :entity entity :invalid-updates invalid-updates})))
|
|
566
|
+
(when (= :write (get-in db [:config :schema-flexibility]))
|
|
567
|
+
(when (or (:db/cardinality entity) (:db/valueType entity))
|
|
568
|
+
(when-not (ds/schema? entity)
|
|
569
|
+
(raise "Incomplete schema transaction attributes, expected :db/ident, :db/valueType, :db/cardinality"
|
|
570
|
+
{:error :transact/schema :entity entity})))))))
|
|
571
|
+
|
|
572
|
+
(defn entity-map->op-vec [db {:keys [tempids] :as report} entity]
|
|
573
|
+
(let [old-eid (:db/id entity)
|
|
574
|
+
tx? (tx-id? old-eid) ;; :db/current-tx / "datomic.tx"
|
|
575
|
+
resolved-eid (cond tx? (current-tx report)
|
|
576
|
+
(sequential? old-eid) (dbu/entid-strict db old-eid)
|
|
577
|
+
(keyword? old-eid) (dbu/entid-strict db [:db/ident old-eid])
|
|
578
|
+
:else old-eid)
|
|
579
|
+
updated-entity (assoc entity :db/id resolved-eid)
|
|
580
|
+
updated-report (cond-> report
|
|
581
|
+
tx? (allocate-eid old-eid resolved-eid))
|
|
582
|
+
resolved-tempid (tempids resolved-eid)
|
|
583
|
+
upserted-eid (upsert-eid db updated-entity tempids)]
|
|
584
|
+
(if (and (some? upserted-eid)
|
|
585
|
+
resolved-tempid
|
|
586
|
+
(not= upserted-eid resolved-tempid))
|
|
587
|
+
{:retry? true :old-eid resolved-eid :upserted-eid upserted-eid}
|
|
588
|
+
(let [new-eid (cond
|
|
589
|
+
(some? upserted-eid) upserted-eid
|
|
590
|
+
(nil? resolved-eid) (next-eid db)
|
|
591
|
+
(tempid? resolved-eid) (or resolved-tempid (next-eid db))
|
|
592
|
+
(number? resolved-eid) resolved-eid
|
|
593
|
+
:else (raise "Expected number, string, keyword or lookup ref for :db/id, got " old-eid
|
|
594
|
+
{:error :entity-id/syntax, :entity updated-entity}))
|
|
595
|
+
new-entity (assoc updated-entity :db/id new-eid)]
|
|
596
|
+
(check-schema-update db updated-entity new-eid)
|
|
597
|
+
{:new-report (allocate-eid updated-report resolved-eid new-eid)
|
|
598
|
+
:new-entities (explode db new-entity)}))))
|
|
599
|
+
|
|
600
|
+
(defn compare-and-swap [db report op-vec]
|
|
601
|
+
(let [[_ e a ov nv] op-vec
|
|
602
|
+
e (dbu/entid-strict db e)
|
|
603
|
+
_ (dbu/validate-attr a op-vec db)
|
|
604
|
+
nv (if (dbu/ref? db a) (dbu/entid-strict db nv) nv)
|
|
605
|
+
datoms (dbi/search db [e a])]
|
|
606
|
+
(if (nil? ov)
|
|
607
|
+
(if (empty? datoms)
|
|
608
|
+
[(transact-add report [:db/add e a nv]) []]
|
|
609
|
+
(raise ":db.fn/cas failed on datom [" e " " a " " (if (dbu/multival? db a) (map :v datoms) (:v (first datoms))) "], expected nil"
|
|
610
|
+
{:error :transact/cas, :old (if (dbu/multival? db a) datoms (first datoms)), :expected ov, :new nv}))
|
|
611
|
+
(let [ov (if (dbu/ref? db a) (dbu/entid-strict db ov) ov)]
|
|
612
|
+
(validate-val nv op-vec db)
|
|
613
|
+
(if (dbu/multival? db a)
|
|
614
|
+
(if (some (fn [^Datom d] (= (.-v d) ov)) datoms)
|
|
615
|
+
[(transact-add report [:db/add e a nv]) []]
|
|
616
|
+
(raise ":db.fn/cas failed on datom [" e " " a " " (map :v datoms) "], expected " ov
|
|
617
|
+
{:error :transact/cas, :old datoms, :expected ov, :new nv}))
|
|
618
|
+
(let [v (:v (first datoms))]
|
|
619
|
+
(if (= v ov)
|
|
620
|
+
[(transact-add report [:db/add e a nv]) []]
|
|
621
|
+
(raise ":db.fn/cas failed on datom [" e " " a " " v "], expected " ov
|
|
622
|
+
{:error :transact/cas, :old (first datoms), :expected ov, :new nv}))))))))
|
|
623
|
+
|
|
624
|
+
(defn retract-entity [db report op-vec]
|
|
625
|
+
(let [[_ e] op-vec]
|
|
626
|
+
(if-let [e (dbu/entid db e)]
|
|
627
|
+
(let [e-datoms (vec (dbi/search db [e]))
|
|
628
|
+
v-datoms (->> (dbi/-attrs-by db :db.type/ref)
|
|
629
|
+
(map (partial dbi/-ident-for db))
|
|
630
|
+
(mapcat (fn [a] (dbi/search db [nil a e])))
|
|
631
|
+
vec)]
|
|
632
|
+
[(reduce transact-retract-datom report (concat e-datoms v-datoms))
|
|
633
|
+
(retract-components db e-datoms)])
|
|
634
|
+
[report []])))
|
|
635
|
+
|
|
636
|
+
(defn check-tuple [db op-vec]
|
|
637
|
+
(let [[_ _ a v] op-vec
|
|
638
|
+
attr-schema (-> db dbi/-schema (get a))]
|
|
639
|
+
(cond (:db/tupleType attr-schema)
|
|
640
|
+
(cond (> (count v) 8)
|
|
641
|
+
(raise "Cannot store more than 8 values for homogeneous tuple: " op-vec
|
|
642
|
+
{:error :transact/syntax, :tx-data op-vec})
|
|
643
|
+
|
|
644
|
+
(not (apply = (map type v)))
|
|
645
|
+
(raise "Cannot store homogeneous tuple with values of different type: " op-vec
|
|
646
|
+
{:error :transact/syntax, :tx-data op-vec})
|
|
647
|
+
|
|
648
|
+
(not (s/valid? (-> db dbi/-schema a :db/tupleType) (first v)))
|
|
649
|
+
(raise "Cannot store homogeneous tuple. Values are of wrong type: " op-vec
|
|
650
|
+
{:error :transact/syntax, :tx-data op-vec}))
|
|
651
|
+
(:db/tupleTypes attr-schema)
|
|
652
|
+
(cond (not (= (count v) (count (:db/tupleTypes attr-schema))))
|
|
653
|
+
(raise (str "Cannot store heterogeneous tuple: expecting " (count (:db/tupleTypes attr-schema)) " values, got " (count v))
|
|
654
|
+
{:error :transact/syntax, :tx-data op-vec})
|
|
655
|
+
|
|
656
|
+
(not (apply = (map s/valid? (:db/tupleTypes attr-schema) v)))
|
|
657
|
+
(raise (str "Cannot store heterogeneous tuple: there is a mismatch between values " v " and their types " (:db/tupleTypes attr-schema))
|
|
658
|
+
{:error :transact/syntax, :tx-data op-vec}))
|
|
659
|
+
(and (:db/tupleAttrs attr-schema)
|
|
660
|
+
(not (::internal (meta op-vec))))
|
|
661
|
+
(raise "Can’t modify tuple attrs directly: " op-vec
|
|
662
|
+
{:error :transact/syntax, :tx-data op-vec}))))
|
|
663
|
+
|
|
664
|
+
(defn- filter-before [datoms ^Date before-date db]
|
|
665
|
+
(let [before-pred (fn [^Datom d]
|
|
666
|
+
(.before ^Date (.-v d) before-date))
|
|
667
|
+
filtered-tx-ids (dbu/filter-txInstant datoms before-pred db)]
|
|
668
|
+
(filter
|
|
669
|
+
(fn [^Datom d]
|
|
670
|
+
(contains? filtered-tx-ids (datom-tx d)))
|
|
671
|
+
datoms)))
|
|
672
|
+
|
|
673
|
+
(defn apply-db-op [db report op-vec]
|
|
674
|
+
(let [[op e a v] op-vec]
|
|
675
|
+
(case op
|
|
676
|
+
|
|
677
|
+
:db/add [(transact-add report op-vec) []]
|
|
678
|
+
|
|
679
|
+
:db/retract (if-some [e (dbu/entid db e)]
|
|
680
|
+
(let [a (dbu/normalize-and-validate-attr a op-vec db)
|
|
681
|
+
pattern (if (nil? v)
|
|
682
|
+
[e a]
|
|
683
|
+
(let [v (if (dbu/ref? db a) (dbu/entid-strict db v) v)]
|
|
684
|
+
(validate-val v op-vec db)
|
|
685
|
+
[e a v]))
|
|
686
|
+
datoms (vec (dbi/search db pattern))]
|
|
687
|
+
[(reduce transact-retract-datom report datoms) []])
|
|
688
|
+
[report []])
|
|
689
|
+
|
|
690
|
+
:db.fn/retractAttribute (if-let [e (dbu/entid db e)]
|
|
691
|
+
(let [a (dbu/normalize-and-validate-attr a op-vec db)
|
|
692
|
+
datoms (vec (dbi/search db [e a]))]
|
|
693
|
+
[(reduce transact-retract-datom report datoms)
|
|
694
|
+
(retract-components db datoms)])
|
|
695
|
+
[report []])
|
|
696
|
+
|
|
697
|
+
:db.fn/retractEntity (retract-entity db report op-vec)
|
|
698
|
+
|
|
699
|
+
:db/retractEntity (retract-entity db report op-vec)
|
|
700
|
+
|
|
701
|
+
:db/purge (if (dbi/-keep-history? db)
|
|
702
|
+
(let [history (HistoricalDB. db)]
|
|
703
|
+
(if-some [e (dbu/entid history e)]
|
|
704
|
+
(let [v (if (dbu/ref? history a) (dbu/entid-strict history v) v)
|
|
705
|
+
old-datoms (dbi/search history [e a v])]
|
|
706
|
+
[(reduce transact-purge-datom report old-datoms) []])
|
|
707
|
+
(raise "Can't find entity with ID " e " to be purged"
|
|
708
|
+
{:error :transact/purge, :operation op, :tx-data op-vec})))
|
|
709
|
+
(raise "Purge is only available in temporal databases."
|
|
710
|
+
{:error :transact/purge :operation op :tx-data op-vec}))
|
|
711
|
+
|
|
712
|
+
:db.purge/attribute (if (dbi/-keep-history? db)
|
|
713
|
+
(let [history (HistoricalDB. db)]
|
|
714
|
+
(if-let [e (dbu/entid history e)]
|
|
715
|
+
(let [datoms (vec (dbi/search history [e a]))]
|
|
716
|
+
[(reduce transact-purge-datom report datoms)
|
|
717
|
+
(purge-components history datoms)])
|
|
718
|
+
(raise "Can't find entity with ID " e " to be purged"
|
|
719
|
+
{:error :transact/purge, :operation op, :tx-data op-vec})))
|
|
720
|
+
(raise "Purge attribute is only available in temporal databases."
|
|
721
|
+
{:error :transact/purge :operation op :tx-data op-vec}))
|
|
722
|
+
|
|
723
|
+
:db.purge/entity (if (dbi/-keep-history? db)
|
|
724
|
+
(let [history (HistoricalDB. db)]
|
|
725
|
+
(if-let [e (dbu/entid history e)]
|
|
726
|
+
(let [e-datoms (vec (dbi/search history [e]))
|
|
727
|
+
v-datoms (vec (mapcat (fn [a] (dbi/search history [nil a e]))
|
|
728
|
+
(dbi/-attrs-by history :db.type/ref)))]
|
|
729
|
+
[(reduce transact-purge-datom report (concat e-datoms v-datoms))
|
|
730
|
+
(purge-components history e-datoms)])
|
|
731
|
+
(raise "Can't find entity with ID " e " to be purged"
|
|
732
|
+
{:error :transact/purge, :operation op, :tx-data op-vec})))
|
|
733
|
+
(raise "Purge entity is only available in temporal databases."
|
|
734
|
+
{:error :transact/purge :operation op :tx-data op-vec}))
|
|
735
|
+
|
|
736
|
+
:db.history.purge/before (if (dbi/-keep-history? db)
|
|
737
|
+
(let [history (HistoricalDB. db)
|
|
738
|
+
into-sorted-set #(apply sorted-set-by dd/cmp-datoms-eavt-quick %)
|
|
739
|
+
e-datoms (-> (clojure.set/difference
|
|
740
|
+
(into-sorted-set (dbs/search-temporal-indices db nil))
|
|
741
|
+
(into-sorted-set (dbs/search-current-indices db nil)))
|
|
742
|
+
(filter-before e db)
|
|
743
|
+
vec)]
|
|
744
|
+
[(reduce transact-purge-datom report e-datoms)
|
|
745
|
+
(purge-components history e-datoms)])
|
|
746
|
+
(raise "Purge entity is only available in temporal databases."
|
|
747
|
+
{:error :transact/purge :operation op :tx-data op-vec}))
|
|
748
|
+
|
|
749
|
+
:db.ensure/attrs (let [{:keys [tx-data]} report
|
|
750
|
+
asserting-datoms (filter (fn [^Datom d] (= e (.-e d))) tx-data)
|
|
751
|
+
asserting-attributes (map (fn [^Datom d] (.-a d)) asserting-datoms)
|
|
752
|
+
diff (clojure.set/difference (set v) (set asserting-attributes))]
|
|
753
|
+
(if (empty? diff)
|
|
754
|
+
[report []]
|
|
755
|
+
(raise "Entity " e " missing attributes " diff " of spec " a
|
|
756
|
+
{:error :transact/ensure :operation op :tx-data op-vec
|
|
757
|
+
:asserting-datoms asserting-datoms})))
|
|
758
|
+
|
|
759
|
+
:db.ensure/preds (let [{:keys [db-after]} report
|
|
760
|
+
preds (assert-preds db-after op-vec)]
|
|
761
|
+
(if-not (empty? preds)
|
|
762
|
+
(raise "Entity " e " failed predicates " preds " of spec " a
|
|
763
|
+
{:error :transact/ensure :operation op :tx-data op-vec})
|
|
764
|
+
[report []]))
|
|
765
|
+
|
|
766
|
+
:db.fn/cas (compare-and-swap db report op-vec)
|
|
767
|
+
|
|
768
|
+
:db/cas (compare-and-swap db report op-vec)
|
|
769
|
+
|
|
770
|
+
:db.fn/call (let [[_ f & args] op-vec]
|
|
771
|
+
[report (apply f db args)])
|
|
772
|
+
|
|
773
|
+
(if (and (keyword? op)
|
|
774
|
+
(not (builtin-op? op)))
|
|
775
|
+
(if-some [ident (dbu/entid db op)]
|
|
776
|
+
(let [fun (-> (dbi/search db [ident :db/fn]) first :v)
|
|
777
|
+
args (next op-vec)]
|
|
778
|
+
(if (fn? fun)
|
|
779
|
+
[report (apply fun db args)]
|
|
780
|
+
(raise "Entity " op " expected to have :db/fn attribute with fn? value"
|
|
781
|
+
{:error :transact/syntax, :operation :db.fn/call, :tx-data op-vec})))
|
|
782
|
+
(raise "Can’t find entity for transaction fn " op
|
|
783
|
+
{:error :transact/syntax, :operation :db.fn/call, :tx-data op-vec}))
|
|
784
|
+
(raise (str "Unknown operation at " op-vec ", expected " (str/join "," builtin-op?)
|
|
785
|
+
" or an ident corresponding to an installed transaction function"
|
|
786
|
+
" (e.g. {:db/ident <keyword> :db/fn <Ifn>}, usage of :db/ident requires {:db/unique :db.unique/identity} in schema)")
|
|
787
|
+
{:error :transact/syntax, :operation op, :tx-data op-vec})))))
|
|
788
|
+
|
|
789
|
+
(defn transact-tx-data [{:keys [db-before] :as initial-report} initial-es]
|
|
790
|
+
(when-not (or (nil? initial-es)
|
|
791
|
+
(sequential? initial-es))
|
|
792
|
+
(raise "Bad transaction data " initial-es ", expected sequential collection"
|
|
793
|
+
{:error :transact/syntax, :tx-data initial-es}))
|
|
794
|
+
(let [has-tuples? (seq (dbi/-attrs-by (:db-after initial-report) :db.type/tuple))
|
|
795
|
+
initial-es' (if has-tuples?
|
|
796
|
+
(interleave initial-es (repeat ::flush-tuples))
|
|
797
|
+
initial-es)
|
|
798
|
+
initial-report (update initial-report :tx-meta
|
|
799
|
+
#(merge {:db/txInstant (get-date)} %))
|
|
800
|
+
meta-entities (flush-tx-meta initial-report)]
|
|
801
|
+
(loop [report (update initial-report :db-after transient)
|
|
802
|
+
es (if (dbi/-keep-history? db-before)
|
|
803
|
+
(concat meta-entities
|
|
804
|
+
initial-es')
|
|
805
|
+
initial-es')]
|
|
806
|
+
(let [[entity & entities] es
|
|
807
|
+
{:keys [tempids db-after]} report
|
|
808
|
+
db db-after]
|
|
809
|
+
(cond
|
|
810
|
+
(empty? es)
|
|
811
|
+
(-> report
|
|
812
|
+
(assoc-in [:tempids :db/current-tx] (current-tx report))
|
|
813
|
+
(update-in [:db-after :max-tx] inc)
|
|
814
|
+
(update :db-after persistent!))
|
|
815
|
+
|
|
816
|
+
(nil? entity)
|
|
817
|
+
(recur report entities)
|
|
818
|
+
|
|
819
|
+
(= ::flush-tuples entity)
|
|
820
|
+
(if (contains? report ::queued-tuples)
|
|
821
|
+
(recur
|
|
822
|
+
(dissoc report ::queued-tuples)
|
|
823
|
+
(concat (flush-tuples report) entities))
|
|
824
|
+
(recur report entities))
|
|
825
|
+
|
|
826
|
+
(map? entity)
|
|
827
|
+
(let [{:keys [new-report new-entities retry? old-eid upserted-eid]} (entity-map->op-vec db report entity)]
|
|
828
|
+
(if retry?
|
|
829
|
+
(retry-with-tempid initial-report report initial-es old-eid upserted-eid)
|
|
830
|
+
(recur new-report (concat new-entities entities))))
|
|
831
|
+
|
|
832
|
+
(sequential? entity)
|
|
833
|
+
(let [[op e a v] entity]
|
|
834
|
+
(when (dbu/tuple? db a)
|
|
835
|
+
(check-tuple db entity))
|
|
836
|
+
(cond
|
|
837
|
+
|
|
838
|
+
(tx-id? e)
|
|
839
|
+
(recur (allocate-eid report e (current-tx report)) (cons [op (current-tx report) a v] entities))
|
|
840
|
+
|
|
841
|
+
(and (dbu/ref? db a) (tx-id? v))
|
|
842
|
+
(recur (allocate-eid report v (current-tx report)) (cons [op e a (current-tx report)] entities))
|
|
843
|
+
|
|
844
|
+
(tempid? e)
|
|
845
|
+
(if (not= op :db/add)
|
|
846
|
+
(raise "Can't use tempid in '" entity "'. Tempids are allowed in :db/add only"
|
|
847
|
+
{:error :transact/syntax, :op entity})
|
|
848
|
+
(let [upserted-eid (when (dbu/is-attr? db a :db.unique/identity)
|
|
849
|
+
(:e (first (dbi/datoms db :avet [a v]))))
|
|
850
|
+
allocated-eid (get tempids e)]
|
|
851
|
+
(if (and upserted-eid allocated-eid (not= upserted-eid allocated-eid))
|
|
852
|
+
(retry-with-tempid initial-report report initial-es e upserted-eid)
|
|
853
|
+
(let [eid (or upserted-eid allocated-eid (next-eid db))]
|
|
854
|
+
(recur (allocate-eid report e eid) (cons [op eid a v] entities))))))
|
|
855
|
+
|
|
856
|
+
(and (dbu/ref? db a) (tempid? v))
|
|
857
|
+
(if-let [vid (get tempids v)]
|
|
858
|
+
(recur report (cons [op e a vid] entities))
|
|
859
|
+
(recur (allocate-eid report v (next-eid db)) es))
|
|
860
|
+
|
|
861
|
+
:else
|
|
862
|
+
(let [[new-report new-entities] (apply-db-op db report entity)]
|
|
863
|
+
(recur new-report (concat new-entities entities)))))
|
|
864
|
+
|
|
865
|
+
(datom? entity)
|
|
866
|
+
(let [[e a v tx added] entity]
|
|
867
|
+
(if added
|
|
868
|
+
(recur (transact-add report [:db/add e a v tx]) entities)
|
|
869
|
+
(recur (transact-retract-datom report entity true) entities)))
|
|
870
|
+
|
|
871
|
+
:else
|
|
872
|
+
(raise "Bad entity type at " entity ", expected map or vector"
|
|
873
|
+
{:error :transact/syntax, :tx-data entity}))))))
|
|
874
|
+
|
|
875
|
+
(defn transact-entities-directly [initial-report initial-es]
|
|
876
|
+
(loop [report (update initial-report :db-after transient)
|
|
877
|
+
es initial-es
|
|
878
|
+
migration-state (or (get-in initial-report [:db-before :migration]) {})]
|
|
879
|
+
(let [[entity & entities] es
|
|
880
|
+
{:keys [config] :as db} (:db-after report)
|
|
881
|
+
[e a v t op] entity
|
|
882
|
+
a-ident (if (and (number? a) (:attribute-refs? config))
|
|
883
|
+
(dbi/-ident-for db a)
|
|
884
|
+
a)
|
|
885
|
+
a (if (:attribute-refs? config)
|
|
886
|
+
(dbi/-ref-for db a-ident)
|
|
887
|
+
(if (number? a)
|
|
888
|
+
(raise "Configuration mismatch: import data with attribute references can not be imported into a database with no attribute references."
|
|
889
|
+
{:error :import/mismatch :data entity})
|
|
890
|
+
a-ident))
|
|
891
|
+
max-eid (next-eid db)
|
|
892
|
+
max-tid (inc (get-in report [:db-after :max-tx]))]
|
|
893
|
+
(cond
|
|
894
|
+
(empty? es)
|
|
895
|
+
(-> report
|
|
896
|
+
(update-in [:db-after :max-tx] inc)
|
|
897
|
+
(update-in [:db-after :migration] #(if %
|
|
898
|
+
(merge % migration-state)
|
|
899
|
+
migration-state))
|
|
900
|
+
(update :db-after persistent!))
|
|
901
|
+
|
|
902
|
+
(= :db.install/attribute a-ident)
|
|
903
|
+
(recur report entities migration-state)
|
|
904
|
+
|
|
905
|
+
;; meta entity
|
|
906
|
+
(ds/meta-attr? a-ident)
|
|
907
|
+
(let [new-t (get-in migration-state [:tids t] max-tid)
|
|
908
|
+
new-datom (dd/datom new-t a v new-t op)
|
|
909
|
+
new-e (.-e new-datom)
|
|
910
|
+
upsert? (not (dbu/multival? db a-ident))]
|
|
911
|
+
(recur (-> (transact-report report new-datom upsert?)
|
|
912
|
+
(assoc-in [:db-after :max-tx] max-tid))
|
|
913
|
+
entities
|
|
914
|
+
(-> migration-state
|
|
915
|
+
(assoc-in [:tids e] new-e)
|
|
916
|
+
(assoc-in [:eids e] new-e))))
|
|
917
|
+
|
|
918
|
+
;; tx not added yet
|
|
919
|
+
(nil? (get-in migration-state [:tids t]))
|
|
920
|
+
(recur (update-in report [:db-after :max-tx] inc) es (assoc-in migration-state [:tids t] max-tid))
|
|
921
|
+
|
|
922
|
+
;; ref not added yet
|
|
923
|
+
(and (dbu/ref? db a) (nil? (get-in migration-state [:eids v])))
|
|
924
|
+
(recur (allocate-eid report max-eid) es (assoc-in migration-state [:eids v] max-eid))
|
|
925
|
+
|
|
926
|
+
:else
|
|
927
|
+
(let [new-datom ^Datom (dd/datom
|
|
928
|
+
(or (get-in migration-state [:eids e]) max-eid)
|
|
929
|
+
a
|
|
930
|
+
(if (dbu/ref? db a)
|
|
931
|
+
(get-in migration-state [:eids v])
|
|
932
|
+
v)
|
|
933
|
+
(get-in migration-state [:tids t])
|
|
934
|
+
op)
|
|
935
|
+
upsert? (and (not (dbu/multival? db a-ident))
|
|
936
|
+
op)]
|
|
937
|
+
(recur (transact-report report new-datom upsert?) entities (assoc-in migration-state [:eids e] (.-e new-datom))))))))
|