Once again generating ADL, and the structure is still better.

This commit is contained in:
Simon Brooke 2018-03-02 18:51:14 +00:00
parent b486ac49e5
commit 0db29228cc
2 changed files with 81 additions and 76 deletions

View file

@ -6,7 +6,8 @@
[clj-time.format :refer [formatters unparse]] [clj-time.format :refer [formatters unparse]]
[squirrel-parse.parser :refer [parse]] [squirrel-parse.parser :refer [parse]]
[squirrel-parse.simplify :refer [simplify]] [squirrel-parse.simplify :refer [simplify]]
[squirrel-parse.utils :refer [deep-merge]])) ;; [squirrel-parse.utils :refer [deep-merge]]
))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -93,15 +94,6 @@
subtree)) subtree))
(defn map-to-xml
[m]
(if
(map? m)
(interleave (keys m) (map map-to-xml (vals m)))
m))
(defn is-create-table-statement? (defn is-create-table-statement?
"Is this statement a create table statement?" "Is this statement a create table statement?"
[statement] [statement]
@ -148,42 +140,40 @@
{(keyword name) {(keyword name)
{:tag :property {:tag :property
:attrs :attrs
{ {:name (get-name column-spec)
:name (get-name column-spec) :type (get-column-datatype column-spec)}}})))
:type (get-column-datatype column-spec)
}}}
)))
(defn make-entity [table-decl] (defn make-entity-map [table-decl]
"Make an ADL entity representing this table declaration" "Make an ADL entity representing this table declaration"
{:tag :entity {:tag :entity
:name (get-name table-decl) :attrs
{:name (get-name table-decl)}
:content :content
{:properties {:properties
(apply (apply
merge merge
(map (map
make-property make-property
(remove (remove
nil? nil?
(map (map
#(if #(if
(is-subtree-of-type? % :COLUMN-SPEC) (is-subtree-of-type? % :COLUMN-SPEC)
%) %)
(get-first-child-of-type table-decl (get-first-child-of-type table-decl
:TABLE-SPEC-ELEMENTS)))))}}) :TABLE-SPEC-ELEMENTS)))))}})
(defn table-definition-to-map (defn table-definition-to-map
"Return a map like this `map` with, if this `statement` is a table declaration, "Return a map like this `map` with, if this `statement` is a table declaration,
an ADL entity representing that table added to it." a map reprentation of an ADL entity representing that table added to it."
[entity-map statement] [entities-map statement]
(if (if
(is-create-table-statement? statement) (is-create-table-statement? statement)
(let [table-name (get-name statement)] (let [table-name (get-name statement)]
(merge entity-map {table-name (make-entity statement)})) (merge entities-map {table-name (make-entity-map statement)}))
entity-map)) entities-map))
(defn is-column-constraint-statement-of-type? (defn is-column-constraint-statement-of-type?
@ -214,61 +204,58 @@
(defn is-primary-key-statement? (defn is-primary-key-statement?
"Returns non-nil (actually the relevant fragment) if `statement` is an
'alter table... add primary key' statement"
[statement] [statement]
(is-column-constraint-statement-of-type? statement :PRIMARY-CC)) (is-column-constraint-statement-of-type? statement :PRIMARY-CC))
(defn decorate-with-relationship (defn decorate-with-relationship
"If this `statement` is a foreign key statement, return an entity-map like this `entity-map`
but with the relevant property decorated with the appropriate foreign key details"
[entity-map statement] [entity-map statement]
(if (if
(is-foreign-key-statement? statement) (is-foreign-key-statement? statement)
(let [sm (subtree-to-map (is-foreign-key-statement? statement)) (let [sm (subtree-to-map (is-foreign-key-statement? statement))
table (:name entity-map) table (:name (:attrs entity-map))
ns-table (:NAME (:QUAL-NAME (:ALTER-TABLE (subtree-to-map statement)))) ns-table (:NAME (:QUAL-NAME (:ALTER-TABLE (subtree-to-map statement))))
ns-cols (:NAME (:NAMES sm)) ns-cols (:NAME (:NAMES sm))
fs-table (:NAME (:REFERENCES-CC sm)) fs-table (:NAME (:REFERENCES-CC sm))
fs-cols (:NAME (:NAMES (:REFERENCES-CC sm)))] fs-cols (:NAME (:NAMES (:REFERENCES-CC sm)))]
(println (str (if
"table: " table (= table ns-table)
"\n\tns-table: " ns-table (do
"\n\tns-cols: " ns-cols (println "...Firing!")
"\n\tfs-table: " fs-table
"\n\tfs-cols: " fs-cols))
(cond
(not
(= table ns-table))
;; this statement doesn't refer to us...
entity-map
true
(deep-merge (deep-merge
entity-map entity-map
{:content {:content
{:properties {:properties
{(keyword ns-cols) {(keyword ns-cols)
{:attrs {:attrs
{:type "entity" :entity fs-table :farkey fs-cols}}}}}))))) {:type "entity" :entity fs-table :farkey fs-cols}}}}}))
;; else this statement doesn't refer to us...
))))
(defn decorate-with-primary-key (defn decorate-with-primary-key
"If this `statement` is a primary key statement, return an entity-map like this `entity-map`
but with the relevant property removed from the 'content' sub-element and added to the 'key'
sub-element."
[entity-map statement] [entity-map statement]
(if (if
(is-primary-key-statement? statement) (is-primary-key-statement? statement)
(let [sm (subtree-to-map (is-primary-key-statement? statement)) (let [sm (subtree-to-map (is-primary-key-statement? statement))
table (:name entity-map) table (:name (:attrs entity-map))
st-table (:NAME (:QUAL-NAME (:ALTER-TABLE (subtree-to-map statement)))) st-table (:NAME (:QUAL-NAME (:ALTER-TABLE (subtree-to-map statement))))
col (keyword (:NAME (:NAMES (:INDEX-PARAMS sm)))) col (keyword (:NAME (:NAMES (:INDEX-PARAMS sm))))
properties (:properties (:content entity-map))] properties (:properties (:content entity-map))]
(if (if
(= table st-table) (= table st-table)
(merge (deep-merge
entity-map entity-map
{:content {:content
(merge {:properties
(:content entity-map) {col {:tag :key}}}})))))
{:key {col (properties col)}
:properties (dissoc properties col)})})
entity-map))
entity-map))
(defn decorate-with-all (defn decorate-with-all
@ -278,7 +265,7 @@
entity-map entity-map
(remove (remove
nil? nil?
(map function statements)))) (map #(apply function (list entity-map %)) statements))))
(defn table-definitions-to-entities (defn table-definitions-to-entities
@ -294,12 +281,34 @@
(let [entity-map (base-map x)] (let [entity-map (base-map x)]
{x {x
(decorate-with-all (decorate-with-all
(decorate-with-all entity-map statements #(decorate-with-relationship entity-map %)) (decorate-with-all entity-map statements #(decorate-with-relationship %1 %2))
statements statements
#(decorate-with-primary-key entity-map %))})) #(decorate-with-primary-key %1 %2))}))
(keys base-map))))) (keys base-map)))))
(defn to-adl-xml
[object]
(cond
(keyword? object)
object
(seq? object)
(vec (map to-adl-xml object))
(map? object)
(case
(:tag object)
:entity
(merge
object
{:content
(map
to-adl-xml
(vals (:properties (:content object))))})
(apply assoc (cons {} (interleave (keys object) (map to-adl-xml (vals object))))))
true
object))
(defn to-adl (defn to-adl
"Take this `input` (filename, url, whatever) assumed to contain a stream of SQL "Take this `input` (filename, url, whatever) assumed to contain a stream of SQL
statements; convert them to ADL with this `application-name`; if `version` is statements; convert them to ADL with this `application-name`; if `version` is
@ -312,7 +321,7 @@
{:tag :application {:tag :application
:attrs {:name application-name :attrs {:name application-name
:version version } :version version }
:content (vals entities)})) :content (to-adl-xml (vals entities))}))
([input application-name version output] ([input application-name version output]
(let [adl (to-adl input application-name version)] (let [adl (to-adl input application-name version)]
(spit output (str xml-header "\n" (with-out-str (emit-element adl)))) (spit output (str xml-header "\n" (with-out-str (emit-element adl))))

View file

@ -28,21 +28,17 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn deep-merge [v & vs] (defn deep-merge [a b]
"Filched from https://gist.github.com/danielpcox/c70a8aa2c36766200a95" "Merge maps `a` and `b`, recursively. If you can't find an online
(letfn [(rec-merge [v1 v2] implementation which actually works, do it yourself."
(if (and (map? v1) (map? v2)) (cond
(merge-with deep-merge v1 v2) (= a b)
v2))] a
(when (some identity vs) (and (map? a) (map? b))
(reduce #(rec-merge %1 %2) v vs)))) (merge-with deep-merge a b)
;; (letfn [(rec-merge [v1 v2] true
;; (if (and (map? v1) (map? v2)) b))
;; (merge-with deep-merge v1 v2)
;; v2))]
;; (if (some identity vs)
;; (reduce #(rec-merge %1 %2) v vs)
;; v)))
(defn- make-unterminated-case-insensitive-match-rule (defn- make-unterminated-case-insensitive-match-rule
"Make a grammar rule which matches this `token` case-insensitively, "Make a grammar rule which matches this `token` case-insensitively,