From e70e3713fc0187a47799c9dc39ccf122d2fa2ee8 Mon Sep 17 00:00:00 2001 From: Hannu Hartikainen Date: Fri, 8 Dec 2023 11:21:25 +0200 Subject: [PATCH 1/2] Implement CREATE INDEX Fixes #348. --- src/honey/sql.cljc | 13 +++++++++++++ src/honey/sql/helpers.cljc | 14 ++++++++++++++ test/honey/sql/helpers_test.cljc | 19 +++++++++++++++++++ test/honey/sql_test.cljc | 3 +-- 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/honey/sql.cljc b/src/honey/sql.cljc index fdf7f2f..67abc90 100644 --- a/src/honey/sql.cljc +++ b/src/honey/sql.cljc @@ -51,6 +51,7 @@ :create-extension :drop-table :drop-view :drop-materialized-view :drop-extension :refresh-materialized-view + :create-index ;; then SQL clauses in priority order: :raw :nest :with :with-recursive :intersect :union :union-all :except :except-all :table @@ -1255,6 +1256,17 @@ (into more) (conj (when as (sql-kw as))))))])) +(defn- format-create-index [k clauses] + (let [[index-spec [table & exprs]] clauses + [pre entity ine & more] (destructure-ddl-item index-spec (str (sql-kw k) " options")) + [sqls params] (format-expr-list exprs)] + (into [(str/join " " (remove empty? + (-> ["CREATE" pre "INDEX" ine entity + "ON" (format-entity table) + (str "(" (str/join ", " sqls) ")")] + (into more))))] + params))) + (defn- format-with-data [_ data] (let [data (if (sequential? data) (first data) data)] [(str/join " " (remove nil? @@ -1433,6 +1445,7 @@ :drop-view #'format-drop-items :drop-materialized-view #'format-drop-items :refresh-materialized-view (fn [_ x] (format-create :refresh :materialized-view x nil)) + :create-index #'format-create-index :raw (fn [_ x] (raw-render x)) :nest (fn [_ x] (let [[sql & params] (format-dsl x {:nested true})] diff --git a/src/honey/sql/helpers.cljc b/src/honey/sql/helpers.cljc index c96960c..7429ec8 100644 --- a/src/honey/sql/helpers.cljc +++ b/src/honey/sql/helpers.cljc @@ -375,6 +375,20 @@ [& views] (generic :refresh-materialized-view views)) +(defn create-index + "Accepts an index spexification and a column specification. The column + specification consists of table name and one or more columns. + + (create-index :name-of-idx [:table :col]) + (create-index :name-of-idx [:table :col1 :col2]) + (create-index [:unique :name-of-idx] [:table :col]) + + PostgreSQL also supports :if-not-exists and expressions instead of columns. + + (create-index [:name-of-idx :if-not-exists] [:table :%lower.col])" + [& args] + (generic :create-index args)) + (defn with "Accepts one or more CTE definitions. diff --git a/test/honey/sql/helpers_test.cljc b/test/honey/sql/helpers_test.cljc index 245f486..b551a6e 100644 --- a/test/honey/sql/helpers_test.cljc +++ b/test/honey/sql/helpers_test.cljc @@ -7,6 +7,7 @@ [honey.sql.helpers :as h :refer [add-column add-index alter-table columns create-table create-table-as create-view create-materialized-view drop-view drop-materialized-view + create-index bulk-collect-into cross-join do-update-set drop-column drop-index drop-table filter from full-join @@ -962,3 +963,21 @@ (is (= '{} (-> '{} (where)))))) + +(deftest test-create-index + (testing "create index, commonly supported features" + (is (= ["CREATE INDEX my_column_idx ON my_table (my_column)"] + (sql/format {:create-index [:my-column-idx [:my-table :my-column]]}))) + (is (= ["CREATE INDEX my_column_idx ON my_table (my_column)"] + (sql/format (create-index :my-column-idx [:my-table :my-column])))) + (is (= ["CREATE UNIQUE INDEX my_column_idx ON my_table (my_column)"] + (sql/format (create-index [:unique :my-column-idx] [:my-table :my-column])))) + (is (= ["CREATE INDEX my_column_idx ON my_table (my_column, my_other_column)"] + (sql/format (create-index :my-column-idx [:my-table :my-column :my-other-column]))))) + (testing "PostgreSQL extensions (IF NOT EXISTS and expressions)" + (is (= ["CREATE INDEX IF NOT EXISTS my_column_idx ON my_table (my_column)"] + (sql/format (create-index [:my-column-idx :if-not-exists] [:my-table :my-column])))) + (is (= ["CREATE UNIQUE INDEX IF NOT EXISTS my_column_idx ON my_table (my_column)"] + (sql/format (create-index [:unique :my-column-idx :if-not-exists] [:my-table :my-column])))) + (is (= ["CREATE INDEX my_column_idx ON my_table (LOWER(my_column))"] + (sql/format (create-index :my-column-idx [:my-table :%lower.my-column])))))) diff --git a/test/honey/sql_test.cljc b/test/honey/sql_test.cljc index df52f92..7386fa0 100644 --- a/test/honey/sql_test.cljc +++ b/test/honey/sql_test.cljc @@ -1311,5 +1311,4 @@ ORDER BY id = ? DESC (comment ;; partial workaround for #407: - (sut/format {:select :f.* :from [[:foo [:f :for :system-time]]] :where [:= :f.id 1]}) - ) + (sut/format {:select :f.* :from [[:foo [:f :for :system-time]]] :where [:= :f.id 1]})) From 45d1230102988ca8c3748e432704190e58928a23 Mon Sep 17 00:00:00 2001 From: Hannu Hartikainen Date: Fri, 8 Dec 2023 13:25:10 +0200 Subject: [PATCH 2/2] Add documentation for create-index --- doc/clause-reference.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/doc/clause-reference.md b/doc/clause-reference.md index 649934f..44d7155 100644 --- a/doc/clause-reference.md +++ b/doc/clause-reference.md @@ -98,7 +98,7 @@ names: the "from" and the "to" names. > Note: `:modify-column` is MySQL-specific and should be considered legacy and deprecated. `:alter-column` will produce `MODIFY COLUMN` when the MySQL dialect is selected. -### add-index, drop-index +### add-index, drop-index, create-index Used with `:alter-table`, `:add-index` accepts a single (function) expression @@ -125,6 +125,22 @@ user=> (-> (h/alter-table :fruit) ["ALTER TABLE fruit ADD PRIMARY KEY(id)"] ``` +Some databases treat the standalone `:create-index` differently (e.g. PostgreSQL) while some treat it as an alias to `:alter-table` `:add-index` (e.g. MySQL). It accepts a pair of index specification and column specification: + +```clojure +user=> (sql/format {:create-index [:my-idx [:fruit :appearance]]}) +["CREATE INDEX my_idx ON fruit (appearance)"] +user=> (sql/format {:create-index [[:unique :another-idx] [:fruit :color :appearance]]}) +["CREATE UNIQUE INDEX another_idx ON fruit (color, appearance)"] +``` + +PostgreSQL supports IF NOT EXISTS and expressions instead of columns. This may make `:create-index` more useful than `:add-index`: + +```clojure +user=> (sql/format (h/create-index [:unique :another-idx :if-not-exists] [:fruit :color :%lower.appearance])) +["CREATE UNIQUE INDEX IF NOT EXISTS another_idx ON fruit (color, LOWER(appearance))"] +``` + ### rename-table Used with `:alter-table`,