Skip to content

Commit

Permalink
change :' to use format-entity #352
Browse files Browse the repository at this point in the history
  • Loading branch information
seancorfield committed Feb 3, 2022
1 parent b7b1eba commit c7c634d
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 41 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Changes

* 2.2.next in progress
* Address [#352](https://github.com/seancorfield/honeysql/issues/352) by treating `:'` as introducing a function name that should be transcribed exactly as-is into the generated SQL.
* Address [#352](https://github.com/seancorfield/honeysql/issues/352) by treating `:'` as introducing a function name that should be formatted as a SQL entity (respects quoting, dot-splitting, etc).

* 2.2.861 -- 2022-01-30
* Address [#382](https://github.com/seancorfield/honeysql/issues/382) by adding `:case-expr` for BigQuery support.
Expand Down
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -574,15 +574,24 @@ regular function calls in a select:
=> ["SELECT MAX(id) FROM foo"]
```

If a keyword begins with `'`, the function name is treated literally
without being converted to uppercase (and without hyphens `-` becoming
spaces):
If a keyword begins with `'`, the function name is formatted as a SQL
entity rather than being converted to uppercase and having hyphens `-`
converted to spaces). That means that hyphens `-` will become underscores `_`
unless you have quoting enabled:

```clojure
(-> (select :*) (from :foo)
(where [:'my-schema.SomeFunction :bar 0])
(sql/format))
=> ["SELECT * FROM foo WHERE my-schema.SomeFunction(bar, ?)" 0]
=> ["SELECT * FROM foo WHERE my_schema.SomeFunction(bar, ?)" 0]
(-> (select :*) (from :foo)
(where [:'my-schema.SomeFunction :bar 0])
(sql/format :quoted true))
=> ["SELECT * FROM \"foo\" WHERE \"my-schema\".\"SomeFunction\"(\"bar\", ?)" 0]
(-> (select :*) (from :foo)
(where [:'my-schema.SomeFunction :bar 0])
(sql/format :dialect :mysql))
=> ["SELECT * FROM `foo` WHERE `my-schema`.`SomeFunction`(`bar`, ?)" 0]
```

### Bindable parameters
Expand Down
66 changes: 33 additions & 33 deletions src/honey/sql.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -164,31 +164,6 @@
s
(recur (str/replace s #"(\w)-(\w)" "$1 $2") s))))

(defn sql-kw
"Given a keyword, return a SQL representation of it as a string.
A keyword whose name begins with a single quote is left exactly as-is
(with the `:` and `'` removed), otherwise a `:kebab-case` keyword
becomes a `KEBAB CASE` (uppercase) string with hyphens replaced
by spaces, e.g., `:insert-into` => `INSERT INTO`.
Any namespace qualifier is ignored."
[k]
(let [n (name k)]
(if (= \' (first n))
(subs n 1 (count n))
(-> n (dehyphen) (upper-case)))))

(defn- sym->kw
"Given a symbol, produce a keyword, retaining the namespace
qualifier, if any."
[s]
(if (symbol? s)
(if-let [n (namespace s)]
(keyword n (name s))
(keyword (name s)))
s))

(defn- namespace-_
"Return the namespace portion of a symbol, with dashes converted."
[x]
Expand All @@ -211,14 +186,6 @@
{:symbol x
:failure (str t)})))))

(defn- sqlize-value [x]
(cond
(nil? x) "NULL"
(string? x) (str \' (str/replace x "'" "''") \')
(ident? x) (sql-kw x)
(vector? x) (str "[" (str/join ", " (map #'sqlize-value x)) "]")
:else (str x)))

(defn format-entity
"Given a simple SQL entity (a keyword or symbol -- or string),
return the equivalent SQL fragment (as a string -- no parameters).
Expand Down Expand Up @@ -255,6 +222,39 @@
[v a d (format-entity v {:aliased a :drop-ns d})])))
.)

(defn sql-kw
"Given a keyword, return a SQL representation of it as a string.
A keyword whose name begins with a single quote is left exactly as-is
(with the `:` and `'` removed), otherwise a `:kebab-case` keyword
becomes a `KEBAB CASE` (uppercase) string with hyphens replaced
by spaces, e.g., `:insert-into` => `INSERT INTO`.
Any namespace qualifier is ignored."
[k]
(let [n (name k)]
(if (= \' (first n))
(format-entity (keyword (subs n 1 (count n))))
(-> n (dehyphen) (upper-case)))))

(defn- sym->kw
"Given a symbol, produce a keyword, retaining the namespace
qualifier, if any."
[s]
(if (symbol? s)
(if-let [n (namespace s)]
(keyword n (name s))
(keyword (name s)))
s))

(defn- sqlize-value [x]
(cond
(nil? x) "NULL"
(string? x) (str \' (str/replace x "'" "''") \')
(ident? x) (sql-kw x)
(vector? x) (str "[" (str/join ", " (map #'sqlize-value x)) "]")
:else (str x)))

(defn- param-value [k]
(if (contains? *params* k)
(get *params* k)
Expand Down
10 changes: 7 additions & 3 deletions test/honey/sql_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -800,10 +800,14 @@ ORDER BY id = ? DESC
(format {:select [[[:'sysdate]]]})))
(is (= ["SELECT count(*)"]
(format {:select [[[:'count :*]]]})))
(is (= ["SELECT Mixed-Kebab(`yum-yum`)"]
(is (= ["SELECT Mixed_Kebab(yum_yum)"]
(format {:select [[[:'Mixed-Kebab :yum-yum]]]})))
(is (= ["SELECT `Mixed-Kebab`(`yum-yum`)"]
(format {:select [[[:'Mixed-Kebab :yum-yum]]]} :dialect :mysql)))
(is (= ["SELECT other-project.other_dataset.other_function(?, ?)" 1 2]
(format {:select [[[:'other-project.other_dataset.other_function 1 2]]]})))))
(is (= ["SELECT other_project.other_dataset.other_function(?, ?)" 1 2]
(format {:select [[[:'other-project.other_dataset.other_function 1 2]]]})))
(is (= ["SELECT \"other-project\".\"other_dataset\".\"other_function\"(?, ?)" 1 2]
(format {:select [[[:'other-project.other_dataset.other_function 1 2]]]} :dialect :ansi)))))

(deftest join-without-on-using
;; essentially issue 326
Expand Down

0 comments on commit c7c634d

Please sign in to comment.