diff --git a/CHANGELOG.md b/CHANGELOG.md index 231fe16..7f93893 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Changes * 2.2.next in progress - * Address #281 by adding support for `SELECT * EXCEPT ..` and `SELECT * REPLACE ..` -- see [SQL Clause Reference - SELECT](https://cljdoc.org/d/com.github.seancorfield/honeysql/CURRENT/doc/getting-started/sql-clause-reference#select-select-distinct) for more details. + * Address #281 by adding support for `SELECT * EXCEPT ..` and `SELECT * REPLACE ..` and `ARRAY<>` and `STRUCT<>` column types -- see [SQL Clause Reference - SELECT](https://cljdoc.org/d/com.github.seancorfield/honeysql/CURRENT/doc/getting-started/sql-clause-reference#select-select-distinct) and [SQL Clause Reference - DDL](https://cljdoc.org/d/com.github.seancorfield/honeysql/CURRENT/doc/getting-started/sql-clause-reference#ddl-clauses) respectively for more details. * Update `build-clj` to v0.6.7. * 2.2.840 -- 2021-12-23 diff --git a/doc/clause-reference.md b/doc/clause-reference.md index a8525dd..b598d64 100644 --- a/doc/clause-reference.md +++ b/doc/clause-reference.md @@ -30,6 +30,8 @@ Several of these include column specifications and HoneySQL provides some special syntax (functions) to support that. See [Column Descriptors in Special Syntax](special-syntax.md#column-descriptors) for more details. +> Google BigQuery support: `[:bigquery/array :string]` as a column type produces `ARRAY` and `[:bigquery/struct col1-spec col2-spec]` as a column type produces `STRUCT` (where `colN-spec` is a vector specifying a named column). + ## alter-table, add-column, drop-column, modify-column, rename-column `:alter-table` can accept either a single table name or diff --git a/src/honey/sql.cljc b/src/honey/sql.cljc index 1820e51..e680632 100644 --- a/src/honey/sql.cljc +++ b/src/honey/sql.cljc @@ -840,9 +840,16 @@ (let [[if-exists tables & more] (destructure-drop-items params "DROP options")] [(str/join " " (remove nil? (into [(sql-kw k) if-exists tables] more)))])) +(def ^:private ^:dynamic *formatted-column* (atom false)) + (defn- format-single-column [xs] - (str/join " " (let [[id & spec] (map #(format-simple-expr % "column operation") xs)] - (cons id (map upper-case spec))))) + (reset! *formatted-column* true) + (str/join " " (cons (format-simple-expr (first xs) "column operation") + (map #(binding [*formatted-column* (atom false)] + (cond-> (format-simple-expr % "column operation") + (not @*formatted-column*) + (upper-case))) + (rest xs))))) (defn- format-table-columns [_ xs] [(str "(" @@ -1139,6 +1146,15 @@ ;; used in DDL to force rendering as a SQL entity instead ;; of a SQL keyword: :entity (fn [_ [e]] [(format-entity e)]) + ;; bigquery column types: + :bigquery/array (fn [_ spec] + [(str "ARRAY<" + (str/join " " (map #(format-simple-expr % "column operation") spec)) + ">")]) + :bigquery/struct (fn [_ spec] + [(str "STRUCT<" + (str/join ", " (map format-single-column spec)) + ">")]) :array (fn [_ [arr]] (let [[sqls params] (format-expr-list arr)] diff --git a/test/honey/bigquery_test.cljc b/test/honey/bigquery_test.cljc index 0c962fd..b117360 100644 --- a/test/honey/bigquery_test.cljc +++ b/test/honey/bigquery_test.cljc @@ -29,3 +29,16 @@ (sut/format {:select [[:* :except]]}))) (is (thrown? ExceptionInfo (sut/format {:select [[:foo :bar :quux]]})))) + +(deftest struct-array-tests + (is (= ["CREATE TABLE IF NOT EXISTS my_table (name STRING NOT NULL, my_struct STRUCT, my_array ARRAY)"] + (sut/format (-> {:create-table [:my-table :if-not-exists] + :with-columns + [[:name :string [:not nil]] + [:my_struct [:bigquery/struct [:name :string [:not nil]] [:description :string]]] + [:my_array [:bigquery/array :string]]]})))) + (is (= ["ALTER TABLE my_table ADD COLUMN IF NOT EXISTS name STRING, ADD COLUMN IF NOT EXISTS my_struct STRUCT, ADD COLUMN IF NOT EXISTS my_array ARRAY"] + (sut/format {:alter-table [:my-table + {:add-column [:name :string :if-not-exists]} + {:add-column [:my_struct [:bigquery/struct [:name :string] [:description :string]] :if-not-exists]} + {:add-column [:my_array [:bigquery/array :string] :if-not-exists]}]}))))