diff --git a/src/datomish/db.cljc b/src/datomish/db.cljc index 21859713..4f308092 100644 --- a/src/datomish/db.cljc +++ b/src/datomish/db.cljc @@ -144,13 +144,35 @@ ] rowid))) + +(defn datoms-attribute-transform + [db x] + {:pre [(db? db)]} + (entid db x)) + +(defn datoms-constant-transform + [db x] + {:pre [(db? db)]} + (sqlite-schema/->SQLite x)) + +(defn datoms-source [db] + (source/map->DatomsSource + {:table :datoms + :fulltext-table :fulltext_values + :fulltext-view :all_datoms + :columns [:e :a :v :tx :added] + :attribute-transform (partial datoms-attribute-transform db) + :constant-transform (partial datoms-constant-transform db) + :table-alias source/gensym-table-alias + :make-constraints nil})) + (defrecord DB [sqlite-connection schema entids ident-map current-tx] ;; ident-map maps between keyword idents and integer entids. The set of idents and entids is ;; disjoint, so we represent both directions of the mapping in the same map for simplicity. Also ;; for simplicity, we assume that an entid has at most one associated ident, and vice-versa. See ;; http://docs.datomic.com/identity.html#idents. IDB - (query-context [db] (context/->Context (source/datoms-source db) nil nil)) + (query-context [db] (context/->Context (datoms-source db) nil nil)) (schema [db] (.-schema db)) diff --git a/src/datomish/query.cljc b/src/datomish/query.cljc index ac7c13c3..2279c996 100644 --- a/src/datomish/query.cljc +++ b/src/datomish/query.cljc @@ -116,17 +116,18 @@ [q] (dp/parse-query q)) -(comment - (def sql-quoting-style nil) - (datomish.query/find->sql-string - (datomish.query.context/->Context (datomish.query.source/datoms-source nil) nil nil) - (datomish.query/parse - '[:find ?timestampMicros ?page :in $ ?latest :where - [?page :page/starred true ?t] - [?t :db/txInstant ?timestampMicros] - (not [(> ?t ?latest)]) ]) - {:latest 5}) -) +#_ +(def sql-quoting-style nil) + +#_ +(datomish.query/find->sql-string + (datomish.query.context/->Context (datomish.query.source/datoms-source nil) nil nil) + (datomish.query/parse + '[:find ?timestampMicros ?page :in $ ?latest :where + [?page :page/starred true ?t] + [?t :db/txInstant ?timestampMicros] + (not [(> ?t ?latest)]) ]) + {:latest 5}) #_ (datomish.query/find->sql-string diff --git a/src/datomish/query/clauses.cljc b/src/datomish/query/clauses.cljc index 69920f41..26d55dee 100644 --- a/src/datomish/query/clauses.cljc +++ b/src/datomish/query/clauses.cljc @@ -4,46 +4,46 @@ (ns datomish.query.clauses (:require - [datomish.query.cc :as cc] - [datomish.query.functions :as functions] - [datomish.query.source - :refer [attribute-in-source - constant-in-source - source->from - source->constraints]] - [datomish.util :as util #?(:cljs :refer-macros :clj :refer) [raise raise-str cond-let]] - [datascript.parser :as dp - #?@(:cljs - [:refer - [ - Constant - DefaultSrc - Function - Not - Or - Pattern - Placeholder - PlainSymbol - Predicate - Variable - ]])] - [honeysql.core :as sql] - [clojure.string :as str] - ) + [datomish.query.cc :as cc] + [datomish.query.functions :as functions] + [datomish.query.source + :refer [attribute-in-source + constant-in-source + source->from + source->constraints]] + [datomish.util :as util #?(:cljs :refer-macros :clj :refer) [raise raise-str cond-let]] + [datascript.parser :as dp + #?@(:cljs + [:refer + [ + Constant + DefaultSrc + Function + Not + Or + Pattern + Placeholder + PlainSymbol + Predicate + Variable + ]])] + [honeysql.core :as sql] + [clojure.string :as str] + ) #?(:clj - (:import - [datascript.parser - Constant - DefaultSrc - Function - Not - Or - Pattern - Placeholder - PlainSymbol - Predicate - Variable - ]))) + (:import + [datascript.parser + Constant + DefaultSrc + Function + Not + Or + Pattern + Placeholder + PlainSymbol + Predicate + Variable + ]))) ;; Pattern building is recursive, so we need forward declarations. (declare @@ -114,7 +114,7 @@ (defn- plain-symbol->sql-predicate-symbol [fn] (when-not (instance? PlainSymbol fn) (raise-str "Predicate functions must be named by plain symbols." fn)) - (#{:> :< :=} (keyword (name (:symbol fn))))) + (#{:> :>= :< :<= := :!=} (keyword (name (:symbol fn))))) (defn apply-predicate-clause [cc predicate] (when-not (instance? Predicate predicate) @@ -240,14 +240,14 @@ (defn not-join->where-fragment [not-join] [:not - (if (empty? (:bindings (:cc not-join))) - ;; If the `not` doesn't establish any bindings, it means it only contains - ;; expressions that constrain variables established outside itself. - ;; We can just return an expression. - (cons :and (:wheres (:cc not-join))) + (if (empty? (:bindings (:cc not-join))) + ;; If the `not` doesn't establish any bindings, it means it only contains + ;; expressions that constrain variables established outside itself. + ;; We can just return an expression. + (cons :and (:wheres (:cc not-join))) - ;; If it does establish bindings, then it has to be a subquery. - [:exists (merge {:select [1]} (cc->partial-subquery (:cc not-join)))])]) + ;; If it does establish bindings, then it has to be a subquery. + [:exists (merge {:select [1]} (cc->partial-subquery (:cc not-join)))])]) ;; A simple Or clause is one in which each branch can be evaluated against diff --git a/src/datomish/query/source.cljc b/src/datomish/query/source.cljc index 5c72612b..4a054e53 100644 --- a/src/datomish/query/source.cljc +++ b/src/datomish/query/source.cljc @@ -4,14 +4,14 @@ (ns datomish.query.source (:require - [datomish.query.transforms :as transforms] - [datascript.parser - #?@(:cljs - [:refer [Variable Constant Placeholder]])]) + [datomish.query.transforms :as transforms] + [datascript.parser + #?@(:cljs + [:refer [Variable Constant Placeholder]])]) #?(:clj - (:import [datascript.parser Variable Constant Placeholder]))) + (:import [datascript.parser Variable Constant Placeholder]))) -(defn- gensym-table-alias [table] +(defn gensym-table-alias [table] (gensym (name table))) ;;; @@ -43,25 +43,25 @@ (constant-in-source [source constant])) (defrecord - DatomsSource - [table ; Typically :datoms. - fulltext-table ; Typically :fulltext_values - fulltext-view ; Typically :all_datoms - columns ; e.g., [:e :a :v :tx] + DatomsSource + [table ; Typically :datoms. + fulltext-table ; Typically :fulltext_values + fulltext-view ; Typically :all_datoms + columns ; e.g., [:e :a :v :tx] - ;; `attribute-transform` is a function from attribute to constant value. Used to - ;; turn, e.g., :p/attribute into an interned integer. - ;; `constant-transform` is a function from constant value to constant value. Used to - ;; turn, e.g., the literal 'true' into 1. - attribute-transform - constant-transform + ;; `attribute-transform` is a function from attribute to constant value. Used to + ;; turn, e.g., :p/attribute into an interned integer. + ;; `constant-transform` is a function from constant value to constant value. Used to + ;; turn, e.g., the literal 'true' into 1. + attribute-transform + constant-transform - ;; `table-alias` is a function from table to alias, e.g., :datoms => :datoms1234. - table-alias + ;; `table-alias` is a function from table to alias, e.g., :datoms => :datoms1234. + table-alias - ;; Not currently used. - make-constraints ; ?fn [source alias] => [where-clauses] - ] + ;; Not currently used. + make-constraints ; ?fn [source alias] => [where-clauses] + ] Source (source->from [source attribute] @@ -93,15 +93,3 @@ (constant-in-source [source constant] ((:constant-transform source) constant))) - -(defn datoms-source [db] - (map->DatomsSource - {:table :datoms - :fulltext-table :fulltext_values - :fulltext-view :all_datoms - :columns [:e :a :v :tx :added] - :attribute-transform transforms/attribute-transform-string - :constant-transform transforms/constant-transform-default - :table-alias gensym-table-alias - :make-constraints nil})) - diff --git a/test/datomish/api.cljc b/test/datomish/api.cljc index 5669a2fa..d1155dd0 100644 --- a/test/datomish/api.cljc +++ b/test/datomish/api.cljc @@ -37,3 +37,5 @@ (def entid db/entid) (def ident db/ident) + +(def !]]]) + #?@(:cljs [[datomish.pair-chan] + [datomish.test-macros :refer-macros [deftest-async]] + [datomish.node-tempfile :refer [tempfile]] + [cljs.test :as t :refer-macros [is are deftest testing async]] + [cljs.core.async :as a :refer [!]]])) + #?(:clj + (:import [clojure.lang ExceptionInfo])) + #?(:clj + (:import [datascript.db DB]))) + +#?(:cljs + (def Throwable js/Error)) + +(def test-schema + [{:db/id (d/id-literal :test -1) + :db/ident :x + :db/unique :db.unique/identity + :db/valueType :db.type/integer + :db.install/_attribute :db.part/db} + ]) + +(deftest-async test-q + (with-tempfile [t (tempfile)] + (let [conn ( ?tx ~tx0)] + [(!= ?a ~(d/entid (d/db conn) :db/txInstant))] ;; TODO: map ident->entid for values. + ] {})) + [[101 (d/entid (d/db conn) :x) 505 tx1]]))) ;; TODO: map entid->ident on egress. + (finally + (