diff --git a/.dir-locals.el b/.dir-locals.el index 49c75d2..a29e5cd 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -12,6 +12,7 @@ (whitespace-line-column . 118) (column-enforce-column . 118) (clojure-docstring-fill-column . 118) + (clojure-indent-style . always-align) (eval . (put-clojure-indent 'with-meta '(:form))) (eval . (put-clojure-indent 'with-bindings* '(:form))))) (markdown-mode . ((fill-column . 80) diff --git a/java/com/metabase/macaw/AstWalker.java b/java/com/metabase/macaw/AstWalker.java index e77aa98..2cf2843 100644 --- a/java/com/metabase/macaw/AstWalker.java +++ b/java/com/metabase/macaw/AstWalker.java @@ -228,7 +228,12 @@ public String toString() { } } - public enum QueryContext { + public interface QueueItem { + public String getKey(); + public String getValue(); + } + + public enum QueryContext implements QueueItem { DELETE, ELSE, FROM, @@ -242,16 +247,38 @@ public enum QueryContext { UPDATE, WHERE; - public String toString() { + public String getKey() { + return "query"; + } + + public String getValue() { return name().toUpperCase(); } } + public class SomeContext implements QueueItem { + private String key; + private String value; + + SomeContext(String key, String value) { + this.key = key; + this.value = value; + } + + public String getKey() { + return this.key; + } + + public String getValue() { + return this.value; + } + } + private static final String NOT_SUPPORTED_YET = "Not supported yet."; private Acc acc; private final EnumMap callbacks; - private final Deque contextStack; + private final Deque contextStack; /** * Construct a new walker with the given `callbacks`. The `callbacks` should be a (Clojure) map of CallbackKeys to @@ -262,7 +289,7 @@ public String toString() { public AstWalker(Map rawCallbacks, Acc val) { this.acc = val; this.callbacks = new EnumMap<>(rawCallbacks); - this.contextStack = new ArrayDeque(); + this.contextStack = new ArrayDeque(); } /** @@ -278,11 +305,11 @@ public void invokeCallback(CallbackKey key, Object visitedItem) { } private void pushContext(QueryContext c) { - this.contextStack.push(c.toString()); + this.contextStack.push(c); } - private void pushContext(String s) { - this.contextStack.push(s); + private void pushContext(QueueItem item) { + this.contextStack.push(item); } // This is pure sugar, but it's nice to be symmetrical with pushContext @@ -846,7 +873,7 @@ public void visit(SelectItem item) { var alias = item.getAlias(); if (alias != null) { // FIXME: this is absolutely a hack, what's the best way to get around it? - pushContext("AS " + alias.getName()); + pushContext(new SomeContext("alias", alias.getName())); } item.getExpression().accept(this); if (alias != null) { diff --git a/src/macaw/core.clj b/src/macaw/core.clj index 4d6dc2a..9e95b7b 100644 --- a/src/macaw/core.clj +++ b/src/macaw/core.clj @@ -5,6 +5,7 @@ [macaw.util :as u] [macaw.walk :as mw]) (:import + (com.metabase.macaw AstWalker$QueueItem AstWalker$QueryContext) (net.sf.jsqlparser.expression Alias) (net.sf.jsqlparser.parser CCJSqlParserUtil) (net.sf.jsqlparser.schema Column Table) @@ -19,7 +20,10 @@ ([key-name xf] (fn item-conjer [results component context] (update results key-name conj {:component (xf component) - :context (vec context)})))) + :context (mapv + (fn [^AstWalker$QueueItem x] + [(keyword (.getKey x)) (.getValue x)]) + context)})))) (defn- query->raw-components [^Statement parsed-query] @@ -35,6 +39,13 @@ :tables #{} :table-wildcards #{}})) +(comment + (def p (parsed-query "SELECT cost FROM (SELECT amount AS cost FROM orders)")) + (query->raw-components p) + (query->components p) + (-> (query->raw-components p) :columns first :component (.getFullyQualifiedName true)) + ) + ;;; tables (defn- make-table [^Table t _ctx] @@ -59,9 +70,9 @@ ;;; columns -(defn- maybe-column-alias [ctx] - (when (some-> (first ctx) (str/starts-with? "AS ")) - {:alias (subs (first ctx) 3)})) +(defn- maybe-column-alias [[maybe-alias :as _ctx]] + (when (= (first maybe-alias) :alias) + {:alias (second maybe-alias)})) (defn- maybe-column-table [{:keys [alias->table name->table]} ^Column c] (if-let [t (.getTable c)] @@ -80,9 +91,17 @@ ;;; get them together +(defn- only-query-context [ctx] + (into [] (comp (filter #(= (first %) :query)) + (map second)) + ctx)) + (defn- update-components [f components] - (map #(update % :component f (:context %)) components)) + (map #(-> % + (update :component f (:context %)) + (update :context only-query-context)) + components)) (defn query->components "Given a parsed query (i.e., a [subclass of] `Statement`) return a map with the elements found within it. @@ -102,7 +121,7 @@ data {:alias->table alias-map :name->table table-map}] {:columns (into #{} (update-components (partial make-column data) columns)) - :has-wildcard? (into #{} has-wildcard?) + :has-wildcard? (into #{} (update-components (fn [x & _args] x) has-wildcard?)) :mutation-commands (into #{} mutation-commands) :tables (into #{} (vals table-map)) :table-wildcards (into #{} (update-components (partial resolve-table-name data) table-wildcards))}))