Validate value types.
This commit is contained in:
parent
bceee3b5fb
commit
43423b7d0a
3 changed files with 116 additions and 43 deletions
|
@ -207,6 +207,11 @@
|
||||||
;; TODO: implement support for DB parts?
|
;; TODO: implement support for DB parts?
|
||||||
(def tx0 0x2000000)
|
(def tx0 0x2000000)
|
||||||
|
|
||||||
|
(def default-schema
|
||||||
|
{:db/txInstant {:db/valueType :db.type/integer}
|
||||||
|
:db/ident {:db/valueType :db.type/keyword}
|
||||||
|
})
|
||||||
|
|
||||||
(defn <idents [sqlite-connection]
|
(defn <idents [sqlite-connection]
|
||||||
(go-pair
|
(go-pair
|
||||||
(let [rows (<? (->>
|
(let [rows (<? (->>
|
||||||
|
@ -223,11 +228,11 @@
|
||||||
(go-pair
|
(go-pair
|
||||||
(when-not (= sqlite-schema/current-version (<? (sqlite-schema/<ensure-current-version sqlite-connection)))
|
(when-not (= sqlite-schema/current-version (<? (sqlite-schema/<ensure-current-version sqlite-connection)))
|
||||||
(raise "Could not ensure current SQLite schema version."))
|
(raise "Could not ensure current SQLite schema version."))
|
||||||
(let [idents (into (<? (<idents sqlite-connection)) {:db/txInstant 100 :db/ident 101 :x 102 :y 103})] ;; TODO: pre-populate idents and SQLite tables?
|
(let [idents (into (<? (<idents sqlite-connection)) {:db/txInstant 100 :db/ident 101 :x 102 :y 103 :name 104 :aka 105})] ;; TODO: pre-populate idents and SQLite tables?
|
||||||
(map->DB
|
(map->DB
|
||||||
{:sqlite-connection sqlite-connection
|
{:sqlite-connection sqlite-connection
|
||||||
:idents idents
|
:idents idents
|
||||||
:schema (ds/schema (into {} (map (fn [[k v]] [(k idents) v]) schema)))
|
:schema (ds/schema (into {} (map (fn [[k v]] [(k idents) v]) (merge schema default-schema))))
|
||||||
:current-tx tx0})))))
|
:current-tx tx0})))))
|
||||||
|
|
||||||
(defn connection-with-db [db]
|
(defn connection-with-db [db]
|
||||||
|
@ -288,6 +293,8 @@
|
||||||
[op e a v (or tx current-tx)]))
|
[op e a v (or tx current-tx)]))
|
||||||
|
|
||||||
(defn preprocess [db report]
|
(defn preprocess [db report]
|
||||||
|
{:pre [(db? db) (report? report)]}
|
||||||
|
|
||||||
(let [initial-es (conj (or (:entities report) []) (tx-entity db))]
|
(let [initial-es (conj (or (:entities report) []) (tx-entity db))]
|
||||||
(when-not (sequential? initial-es)
|
(when-not (sequential? initial-es)
|
||||||
(raise "Bad transaction data " initial-es ", expected sequential collection"
|
(raise "Bad transaction data " initial-es ", expected sequential collection"
|
||||||
|
@ -351,6 +358,8 @@
|
||||||
|
|
||||||
|
|
||||||
(defn <resolve-lookup-refs [db report]
|
(defn <resolve-lookup-refs [db report]
|
||||||
|
{:pre [(db? db) (report? report)]}
|
||||||
|
|
||||||
(go-pair
|
(go-pair
|
||||||
(->>
|
(->>
|
||||||
(vec (for [[op & entity] (:entities report)]
|
(vec (for [[op & entity] (:entities report)]
|
||||||
|
@ -409,21 +418,29 @@
|
||||||
simple allocations."
|
simple allocations."
|
||||||
|
|
||||||
[db report]
|
[db report]
|
||||||
|
{:pre [(db? db) (report? report)]}
|
||||||
|
|
||||||
(go-pair
|
(go-pair
|
||||||
(let [keyfn (fn [[op e a v tx]]
|
(let [keyfn (fn [[op e a v tx]]
|
||||||
(if (and (id-literal? e)
|
(if (and (id-literal? e)
|
||||||
(not-any? id-literal? [a v tx]))
|
(not-any? id-literal? [a v tx]))
|
||||||
(- 5)
|
(- 5)
|
||||||
(- (count (filter id-literal? [e a v tx])))))
|
(- (count (filter id-literal? [e a v tx])))))
|
||||||
initial-report (dissoc report :entities) ;; TODO.
|
initial-report (assoc report :entities []) ;; TODO.
|
||||||
initial-entities (sort-by keyfn (:entities report))]
|
initial-entities (sort-by keyfn (:entities report))]
|
||||||
(loop [report initial-report
|
(loop [report initial-report
|
||||||
es initial-entities]
|
es initial-entities]
|
||||||
|
(if (report? initial-report)
|
||||||
|
(update report :tempids #(into {} (filter (comp not temp-literal? first) %)))
|
||||||
|
(raise "fail" {:initial-report report}))
|
||||||
|
|
||||||
(let [[[op e a v tx :as entity] & entities] es]
|
(let [[[op e a v tx :as entity] & entities] es]
|
||||||
(cond
|
(cond
|
||||||
(nil? entity)
|
(nil? entity)
|
||||||
;; We can add :db.part/temp id-literals; remove them.
|
;; We can add :db.part/temp id-literals; remove them.
|
||||||
|
(if (report? report)
|
||||||
(update report :tempids #(into {} (filter (comp not temp-literal? first) %)))
|
(update report :tempids #(into {} (filter (comp not temp-literal? first) %)))
|
||||||
|
(raise "fail" {:report report}))
|
||||||
|
|
||||||
(and (not= op :db/add)
|
(and (not= op :db/add)
|
||||||
(not (empty? (filter id-literal? [e a v tx]))))
|
(not (empty? (filter id-literal? [e a v tx]))))
|
||||||
|
@ -438,9 +455,7 @@
|
||||||
(let [upserted-eid (:e (first (<? (<avet db [a v])))) ;; TODO: define this interface.
|
(let [upserted-eid (:e (first (<? (<avet db [a v])))) ;; TODO: define this interface.
|
||||||
allocated-eid (get-in report [:tempids e])]
|
allocated-eid (get-in report [:tempids e])]
|
||||||
(if (and upserted-eid allocated-eid (not= upserted-eid allocated-eid))
|
(if (and upserted-eid allocated-eid (not= upserted-eid allocated-eid))
|
||||||
(do
|
|
||||||
(<? (<retry-with-tempid db initial-report initial-entities e upserted-eid)) ;; TODO: not initial report, just the sorted entities here.
|
(<? (<retry-with-tempid db initial-report initial-entities e upserted-eid)) ;; TODO: not initial report, just the sorted entities here.
|
||||||
)
|
|
||||||
(let [eid (or upserted-eid allocated-eid (next-eid db))]
|
(let [eid (or upserted-eid allocated-eid (next-eid db))]
|
||||||
(recur (allocate-eid report e eid) (cons [op eid a v tx] entities)))))
|
(recur (allocate-eid report e eid) (cons [op eid a v tx] entities)))))
|
||||||
|
|
||||||
|
@ -471,7 +486,22 @@
|
||||||
(defn- transact-report [report datom]
|
(defn- transact-report [report datom]
|
||||||
(update-in report [:tx-data] conj datom))
|
(update-in report [:tx-data] conj datom))
|
||||||
|
|
||||||
|
(defn- ensure-schema-constraints
|
||||||
|
"Verify that all entities obey the schema constraints."
|
||||||
|
|
||||||
|
[db report]
|
||||||
|
{:pre [(db? db) (report? report)]}
|
||||||
|
|
||||||
|
;; TODO: :db/unique :db.unique/value.
|
||||||
|
;; TODO: consider accumulating errors to show more meaningful error reports.
|
||||||
|
;; TODO: constrain entities; constrain attributes.
|
||||||
|
|
||||||
|
(doseq [[op e a v tx] (:entities report)]
|
||||||
|
(ds/ensure-valid-value (schema db) a v))
|
||||||
|
report)
|
||||||
|
|
||||||
(defn <postprocess [db report]
|
(defn <postprocess [db report]
|
||||||
|
{:pre [(db? db) (report? report)]}
|
||||||
(go-pair
|
(go-pair
|
||||||
(let [initial-report report]
|
(let [initial-report report]
|
||||||
(loop [report initial-report
|
(loop [report initial-report
|
||||||
|
@ -505,11 +535,11 @@
|
||||||
{:error :transact/syntax, :operation op, :tx-data entity})))))))
|
{:error :transact/syntax, :operation op, :tx-data entity})))))))
|
||||||
|
|
||||||
(defn <transact-tx-data
|
(defn <transact-tx-data
|
||||||
[db now initial-report]
|
[db now report]
|
||||||
{:pre [(db? db)]}
|
{:pre [(db? db) (report? report)]}
|
||||||
|
|
||||||
(go-pair
|
(go-pair
|
||||||
(->> initial-report
|
(->> report
|
||||||
(preprocess db)
|
(preprocess db)
|
||||||
|
|
||||||
(<resolve-lookup-refs db)
|
(<resolve-lookup-refs db)
|
||||||
|
@ -518,6 +548,8 @@
|
||||||
(<resolve-id-literals db)
|
(<resolve-id-literals db)
|
||||||
(<?)
|
(<?)
|
||||||
|
|
||||||
|
(ensure-schema-constraints db)
|
||||||
|
|
||||||
(<postprocess db)
|
(<postprocess db)
|
||||||
(<?))))
|
(<?))))
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
|
|
||||||
;; Purloined from DataScript.
|
;; Purloined from DataScript.
|
||||||
|
|
||||||
(ns datomish.schema)
|
(ns datomish.schema
|
||||||
|
(:require [datomish.util :as util #?(:cljs :refer-macros :clj :refer) [raise]]))
|
||||||
|
|
||||||
(defprotocol ISchema
|
(defprotocol ISchema
|
||||||
(attrs-by
|
(attrs-by
|
||||||
|
@ -39,6 +40,9 @@
|
||||||
:cljs [^boolean unique-value?]) [schema attr]
|
:cljs [^boolean unique-value?]) [schema attr]
|
||||||
(is-attr? schema attr :db.unique/value))
|
(is-attr? schema attr :db.unique/value))
|
||||||
|
|
||||||
|
(defn schema? [x]
|
||||||
|
(satisfies? ISchema x))
|
||||||
|
|
||||||
(defrecord Schema [schema rschema]
|
(defrecord Schema [schema rschema]
|
||||||
ISchema
|
ISchema
|
||||||
(attrs-by [schema property]
|
(attrs-by [schema property]
|
||||||
|
@ -76,8 +80,37 @@
|
||||||
:key k
|
:key k
|
||||||
:value v}))))
|
:value v}))))
|
||||||
|
|
||||||
|
;; TODO: consider doing this with a protocol and extending the underlying Clojure(Script) types.
|
||||||
|
(def value-type-map
|
||||||
|
{:db.type/ref { :valid? #(and (integer? %) (pos? %)) :->SQLite identity :<-SQLite identity }
|
||||||
|
:db.type/keyword { :valid? keyword? :->SQLite name :<-SQLite keyword }
|
||||||
|
:db.type/string { :valid? string? :->SQLite identity :<-SQLite identity }
|
||||||
|
:db.type/boolean { :valid? #(instance? Boolean %) :->SQLite #(if % 1 0) :<-SQLite #(if (= % 1) true false) }
|
||||||
|
:db.type/integer { :valid? integer? :->SQLite identity :<-SQLite identity }
|
||||||
|
:db.type/real { :valid? float? :->SQLite identity :<-SQLite identity }
|
||||||
|
})
|
||||||
|
|
||||||
|
(defn #?@(:clj [^Boolean ensure-valid-value]
|
||||||
|
:cljs [^boolean ensure-valid-value]) [schema attr value]
|
||||||
|
{:pre [(schema? schema)]}
|
||||||
|
(let [schema (.-schema schema)]
|
||||||
|
(if-let [valueType (get-in schema [attr :db/valueType])]
|
||||||
|
(if-let [valid? (get-in value-type-map [valueType :valid?])]
|
||||||
|
(when-not (valid? value)
|
||||||
|
(raise "Invalid value for attribute " attr ", expected " valueType " but got " value
|
||||||
|
{:error :schema/valueType, :attribute attr, :value value}))
|
||||||
|
(raise "Unknown valueType for attribute " attr ", expected one of " (sorted-set (keys value-type-map))
|
||||||
|
{:error :schema/valueType, :attribute attr}))
|
||||||
|
(raise "Unknown attribute " attr ", expected one of " (sorted-set (keys schema))
|
||||||
|
{:error :schema/valueType, :attribute attr}))))
|
||||||
|
|
||||||
(defn- validate-schema [schema]
|
(defn- validate-schema [schema]
|
||||||
(doseq [[a kv] schema]
|
(doseq [[a kv] schema]
|
||||||
|
(when-not (:db/valueType kv)
|
||||||
|
(throw (ex-info (str "Bad attribute specification for " a ": should have {:db/valueType ...}")
|
||||||
|
{:error :schema/validation
|
||||||
|
:attribute a
|
||||||
|
:key :db/valueType})))
|
||||||
(let [comp? (:db/isComponent kv false)]
|
(let [comp? (:db/isComponent kv false)]
|
||||||
(validate-schema-key a :db/isComponent (:db/isComponent kv) #{true false})
|
(validate-schema-key a :db/isComponent (:db/isComponent kv) #{true false})
|
||||||
(when (and comp? (not= (:db/valueType kv) :db.type/ref))
|
(when (and comp? (not= (:db/valueType kv) :db.type/ref))
|
||||||
|
@ -86,7 +119,7 @@
|
||||||
:attribute a
|
:attribute a
|
||||||
:key :db/isComponent}))))
|
:key :db/isComponent}))))
|
||||||
(validate-schema-key a :db/unique (:db/unique kv) #{:db.unique/value :db.unique/identity})
|
(validate-schema-key a :db/unique (:db/unique kv) #{:db.unique/value :db.unique/identity})
|
||||||
(validate-schema-key a :db/valueType (:db/valueType kv) #{:db.type/ref})
|
(validate-schema-key a :db/valueType (:db/valueType kv) (set (keys value-type-map)))
|
||||||
(validate-schema-key a :db/cardinality (:db/cardinality kv) #{:db.cardinality/one :db.cardinality/many}))
|
(validate-schema-key a :db/cardinality (:db/cardinality kv) #{:db.cardinality/one :db.cardinality/many}))
|
||||||
schema)
|
schema)
|
||||||
|
|
||||||
|
|
|
@ -53,20 +53,30 @@
|
||||||
(defn tx [report]
|
(defn tx [report]
|
||||||
(get-in report [:db-after :current-tx]))
|
(get-in report [:db-after :current-tx]))
|
||||||
|
|
||||||
|
(def test-schema
|
||||||
|
{:x {:db/unique :db.unique/identity
|
||||||
|
:db/valueType :db.type/integer}
|
||||||
|
:y {:db/cardinality :db.cardinality/many
|
||||||
|
:db/valueType :db.type/integer}
|
||||||
|
:name {:db/unique :db.unique/identity
|
||||||
|
:db/valueType :db.type/string}
|
||||||
|
:aka {:db/cardinality :db.cardinality/many
|
||||||
|
:db/valueType :db.type/string}})
|
||||||
|
|
||||||
(deftest-async test-add-one
|
(deftest-async test-add-one
|
||||||
(with-tempfile [t (tempfile)]
|
(with-tempfile [t (tempfile)]
|
||||||
(let [c (<? (s/<sqlite-connection t))
|
(let [c (<? (s/<sqlite-connection t))
|
||||||
db (<? (dm/<db-with-sqlite-connection c))
|
db (<? (dm/<db-with-sqlite-connection c test-schema))
|
||||||
conn (dm/connection-with-db db)
|
conn (dm/connection-with-db db)
|
||||||
now 0xdeadbeef]
|
now 0xdeadbeef]
|
||||||
(try
|
(try
|
||||||
(let [;; TODO: drop now, allow to set :db/txInstant.
|
(let [;; TODO: drop now, allow to set :db/txInstant.
|
||||||
report (<? (dm/<transact! conn [[:db/add 0 :x "valuex"]] now))
|
report (<? (dm/<transact! conn [[:db/add 0 :name "valuex"]] now))
|
||||||
tx (tx report)]
|
tx (tx report)]
|
||||||
(is (= (<? (<datoms (dm/db conn)))
|
(is (= (<? (<datoms (dm/db conn)))
|
||||||
#{[0 :x "valuex"]}))
|
#{[0 :name "valuex"]}))
|
||||||
(is (= (<? (<transactions (dm/db conn)))
|
(is (= (<? (<transactions (dm/db conn)))
|
||||||
[[0 :x "valuex" tx 1] ;; TODO: true, not 1.
|
[[0 :name "valuex" tx 1] ;; TODO: true, not 1.
|
||||||
[tx :db/txInstant now tx 1]])))
|
[tx :db/txInstant now tx 1]])))
|
||||||
(finally
|
(finally
|
||||||
(<? (dm/close-db db)))))))
|
(<? (dm/close-db db)))))))
|
||||||
|
@ -74,30 +84,28 @@
|
||||||
(deftest-async test-add-two
|
(deftest-async test-add-two
|
||||||
(with-tempfile [t (tempfile)]
|
(with-tempfile [t (tempfile)]
|
||||||
(let [c (<? (s/<sqlite-connection t))
|
(let [c (<? (s/<sqlite-connection t))
|
||||||
db (<? (dm/<db-with-sqlite-connection c
|
db (<? (dm/<db-with-sqlite-connection c test-schema))
|
||||||
{:x {:db/unique :db.unique/identity} ;; TODO: :name and :aka.
|
|
||||||
:y {:db/cardinality :db.cardinality/many}}))
|
|
||||||
conn (dm/connection-with-db db)
|
conn (dm/connection-with-db db)
|
||||||
now 0xdeadbeef]
|
now 0xdeadbeef]
|
||||||
(try
|
(try
|
||||||
(let [tx1 (tx (<? (dm/<transact! conn [[:db/add 1 :x "Ivan"]] now)))
|
(let [tx1 (tx (<? (dm/<transact! conn [[:db/add 1 :name "Ivan"]] now)))
|
||||||
tx2 (tx (<? (dm/<transact! conn [[:db/add 1 :x "Petr"]] now)))
|
tx2 (tx (<? (dm/<transact! conn [[:db/add 1 :name "Petr"]] now)))
|
||||||
tx3 (tx (<? (dm/<transact! conn [[:db/add 1 :y "Tupen"]] now)))
|
tx3 (tx (<? (dm/<transact! conn [[:db/add 1 :aka "Tupen"]] now)))
|
||||||
tx4 (tx (<? (dm/<transact! conn [[:db/add 1 :y "Devil"]] now)))]
|
tx4 (tx (<? (dm/<transact! conn [[:db/add 1 :aka "Devil"]] now)))]
|
||||||
(is (= (<? (<datoms (dm/db conn)))
|
(is (= (<? (<datoms (dm/db conn)))
|
||||||
#{[1 :x "Petr"]
|
#{[1 :name "Petr"]
|
||||||
[1 :y "Tupen"]
|
[1 :aka "Tupen"]
|
||||||
[1 :y "Devil"]}))
|
[1 :aka "Devil"]}))
|
||||||
|
|
||||||
(is (= (<? (<transactions (dm/db conn)))
|
(is (= (<? (<transactions (dm/db conn)))
|
||||||
[[1 :x "Ivan" tx1 1] ;; TODO: true, not 1.
|
[[1 :name "Ivan" tx1 1] ;; TODO: true, not 1.
|
||||||
[tx1 :db/txInstant now tx1 1]
|
[tx1 :db/txInstant now tx1 1]
|
||||||
[1 :x "Ivan" tx2 0]
|
[1 :name "Ivan" tx2 0]
|
||||||
[1 :x "Petr" tx2 1]
|
[1 :name "Petr" tx2 1]
|
||||||
[tx2 :db/txInstant now tx2 1]
|
[tx2 :db/txInstant now tx2 1]
|
||||||
[1 :y "Tupen" tx3 1]
|
[1 :aka "Tupen" tx3 1]
|
||||||
[tx3 :db/txInstant now tx3 1]
|
[tx3 :db/txInstant now tx3 1]
|
||||||
[1 :y "Devil" tx4 1]
|
[1 :aka "Devil" tx4 1]
|
||||||
[tx4 :db/txInstant now tx4 1]])))
|
[tx4 :db/txInstant now tx4 1]])))
|
||||||
(finally
|
(finally
|
||||||
(<? (dm/close-db db)))))))
|
(<? (dm/close-db db)))))))
|
||||||
|
@ -106,18 +114,18 @@
|
||||||
(deftest-async test-retract
|
(deftest-async test-retract
|
||||||
(with-tempfile [t (tempfile)]
|
(with-tempfile [t (tempfile)]
|
||||||
(let [c (<? (s/<sqlite-connection t))
|
(let [c (<? (s/<sqlite-connection t))
|
||||||
db (<? (dm/<db-with-sqlite-connection c))
|
db (<? (dm/<db-with-sqlite-connection c test-schema))
|
||||||
conn (dm/connection-with-db db)
|
conn (dm/connection-with-db db)
|
||||||
now 0xdeadbeef]
|
now 0xdeadbeef]
|
||||||
(try
|
(try
|
||||||
(let [txa (tx (<? (dm/<transact! conn [[:db/add 0 :x "valuex"]] now)))
|
(let [txa (tx (<? (dm/<transact! conn [[:db/add 0 :x 123]] now)))
|
||||||
txb (tx (<? (dm/<transact! conn [[:db/retract 0 :x "valuex"]] now)))]
|
txb (tx (<? (dm/<transact! conn [[:db/retract 0 :x 123]] now)))]
|
||||||
(is (= (<? (<datoms db))
|
(is (= (<? (<datoms db))
|
||||||
#{}))
|
#{}))
|
||||||
(is (= (<? (<transactions db))
|
(is (= (<? (<transactions db))
|
||||||
[[0 :x "valuex" txa 1] ;; TODO: true, not 1.
|
[[0 :x 123 txa 1] ;; TODO: true, not 1.
|
||||||
[txa :db/txInstant now txa 1]
|
[txa :db/txInstant now txa 1]
|
||||||
[0 :x "valuex" txb 0]
|
[0 :x 123 txb 0]
|
||||||
[txb :db/txInstant now txb 1]])))
|
[txb :db/txInstant now txb 1]])))
|
||||||
(finally
|
(finally
|
||||||
(<? (dm/close-db db)))))))
|
(<? (dm/close-db db)))))))
|
||||||
|
@ -125,7 +133,7 @@
|
||||||
(deftest-async test-id-literal-1
|
(deftest-async test-id-literal-1
|
||||||
(with-tempfile [t (tempfile)]
|
(with-tempfile [t (tempfile)]
|
||||||
(let [c (<? (s/<sqlite-connection t))
|
(let [c (<? (s/<sqlite-connection t))
|
||||||
db (<? (dm/<db-with-sqlite-connection c))
|
db (<? (dm/<db-with-sqlite-connection c test-schema))
|
||||||
conn (dm/connection-with-db db)
|
conn (dm/connection-with-db db)
|
||||||
now -1]
|
now -1]
|
||||||
(try
|
(try
|
||||||
|
@ -151,19 +159,19 @@
|
||||||
(deftest-async test-add-ident
|
(deftest-async test-add-ident
|
||||||
(with-tempfile [t (tempfile)]
|
(with-tempfile [t (tempfile)]
|
||||||
(let [c (<? (s/<sqlite-connection t))
|
(let [c (<? (s/<sqlite-connection t))
|
||||||
db (<? (dm/<db-with-sqlite-connection c))
|
db (<? (dm/<db-with-sqlite-connection c test-schema))
|
||||||
conn (dm/connection-with-db db)
|
conn (dm/connection-with-db db)
|
||||||
now -1]
|
now -1]
|
||||||
(try
|
(try
|
||||||
(let [report (<? (dm/<transact! conn [[:db/add 44 :db/ident :name]] now))
|
(let [report (<? (dm/<transact! conn [[:db/add (dm/id-literal :db.part/db -1) :db/ident :test/ident]] now))
|
||||||
db-after (:db-after report)
|
db-after (:db-after report)
|
||||||
tx (:current-tx db-after)]
|
tx (:current-tx db-after)]
|
||||||
(is (= (:name (dm/idents db-after)) 44)))
|
(is (= (:test/ident (dm/idents db-after)) (get-in report [:tempids (dm/id-literal :db.part/db -1)]))))
|
||||||
|
|
||||||
;; TODO: This should fail, but doesn't, due to stringification of :name.
|
;; TODO: This should fail, but doesn't, due to stringification of :test/ident.
|
||||||
;; (is (thrown-with-msg?
|
;; (is (thrown-with-msg?
|
||||||
;; ExceptionInfo #"Retracting a :db/ident is not yet supported, got "
|
;; ExceptionInfo #"Retracting a :db/ident is not yet supported, got "
|
||||||
;; (<? (dm/<transact! conn [[:db/retract 44 :db/ident :name]] now))))
|
;; (<? (dm/<transact! conn [[:db/retract 44 :db/ident :test/ident]] now))))
|
||||||
|
|
||||||
;; ;; Renaming looks like retraction and then assertion.
|
;; ;; Renaming looks like retraction and then assertion.
|
||||||
;; (is (thrown-with-msg?
|
;; (is (thrown-with-msg?
|
||||||
|
@ -172,7 +180,7 @@
|
||||||
|
|
||||||
;; (is (thrown-with-msg?
|
;; (is (thrown-with-msg?
|
||||||
;; ExceptionInfo #"Re-asserting a :db/ident is not yet supported, got"
|
;; ExceptionInfo #"Re-asserting a :db/ident is not yet supported, got"
|
||||||
;; (<? (dm/<transact! conn [[:db/add 55 :db/ident :name]] now))))
|
;; (<? (dm/<transact! conn [[:db/add 55 :db/ident :test/ident]] now))))
|
||||||
|
|
||||||
(finally
|
(finally
|
||||||
(<? (dm/close-db db)))))))
|
(<? (dm/close-db db)))))))
|
||||||
|
|
Loading…
Reference in a new issue