From 99e955f420925f03259b450c665cdb6777325bf1 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sat, 6 Aug 2022 21:49:49 -0700 Subject: [PATCH] fix #419 by adding InlineValue protocol --- CHANGELOG.md | 1 + doc/extending-honeysql.md | 17 +++++++++++++++++ doc/options.md | 2 ++ src/honey/sql.cljc | 31 ++++++++++++++++++++----------- src/honey/sql/protocols.clj | 8 ++++++++ 5 files changed, 48 insertions(+), 11 deletions(-) create mode 100644 src/honey/sql/protocols.clj diff --git a/CHANGELOG.md b/CHANGELOG.md index b6e5edb2..2420cabd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changes * 2.3.next in progress + * Address [#419](https://github.com/seancorfield/honeysql/issues/419) by adding `honey.sql.protocols` and `InlineValue` with a `sqlize` function. * Address [#413](https://github.com/seancorfield/honeysql/issues/413) by flagging a lack of `WHERE` clause for `DELETE`, `DELETE FROM`, and `UPDATE` when `:checking :basic` (or `:checking :strict`). * 2.3.911 -- 2022-07-29 diff --git a/doc/extending-honeysql.md b/doc/extending-honeysql.md index ea4b904a..08daf7c3 100644 --- a/doc/extending-honeysql.md +++ b/doc/extending-honeysql.md @@ -14,6 +14,23 @@ many more. Built in operators include: `:=`, `:+`, `:mod`. Built in functions (special syntax) include: `:array`, `:case`, `:cast`, `:inline`, `:raw` and many more. +## Extending what `:inline` can do + +By default, the `:inline` option can convert a fairly +basic set of values/types to SQL strings: +* `nil` +* strings +* keywords and symbols +* vectors +* UUIDs (Clojure only) + +Everything is naively converted by calling `str`. + +You can extend `honey.sql.protocols/InlineValue` to +other types and defining how the `sqlize` function +should behave. It takes a single argument, the value +to be inlined (converted to a SQL string). + ## Registering a New Clause Formatter `honey.sql/register-clause!` accepts a keyword (or a symbol) diff --git a/doc/options.md b/doc/options.md index d438aa1d..60ff7569 100644 --- a/doc/options.md +++ b/doc/options.md @@ -83,6 +83,8 @@ was wrapped in `[:inline `..`]`: * keywords and symbols become SQL keywords (uppercase, with `-` replaced by a space), * everything else is just turned into a string (by calling `str`) and added to the SQL string. +> Note: you can provide additional inline formatting by extending the `InlineValue` protocol from `honey.sql.protocols` to new types. + ## `:params` The `:params` option provides a mapping from named parameters diff --git a/src/honey/sql.cljc b/src/honey/sql.cljc index ea80858e..2c819ea1 100644 --- a/src/honey/sql.cljc +++ b/src/honey/sql.cljc @@ -28,7 +28,8 @@ * `sql-kw` -- turns a Clojure keyword (or symbol) into SQL code (makes it uppercase and replaces - with space). " (:refer-clojure :exclude [format]) - (:require [clojure.string :as str])) + (:require [clojure.string :as str] + [honey.sql.protocols :as p])) ;; default formatting for known clauses @@ -263,16 +264,24 @@ (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)) "]") - ;; issue 385: quoted UUIDs for PostgreSQL/ANSI - #?(:clj (instance? java.util.UUID x) :cljs false) - (str \' x \') ; UUID cannot contain quotes - :else (str x))) +(extend-protocol p/InlineValue + nil + (sqlize [_] "NULL") + String + (sqlize [x] (str \' (str/replace x "'" "''") \')) + #?(:clj clojure.lang.Keyword :cljs Keyword) + (sqlize [x] (sql-kw x)) + #?(:clj clojure.lang.Symbol :cljs Symbol) + (sqlize [x] (sql-kw x)) + #?(:clj clojure.lang.IPersistentVector :cljs PersistentVector) + (sqlize [x] (str "[" (str/join ", " (map p/sqlize x)) "]")) + #?@(:clj [java.util.UUID + ;; issue 385: quoted UUIDs for PostgreSQL/ANSI + (sqlize [x] (str \' x \'))]) + Object + (sqlize [x] (str x))) + +(defn- sqlize-value [x] (p/sqlize x)) (defn- param-value [k] (if (contains? *params* k) diff --git a/src/honey/sql/protocols.clj b/src/honey/sql/protocols.clj new file mode 100644 index 00000000..4cf60812 --- /dev/null +++ b/src/honey/sql/protocols.clj @@ -0,0 +1,8 @@ +;; copyright (c) 2022 sean corfield, all rights reserved + +(ns honey.sql.protocols + "InlineValue -- a protocol that defines how to inline + values; (sqlize x) produces a SQL string for x.") + +(defprotocol InlineValue :extend-via-metadata true + (sqlize [this] "Render value inline in a SQL string."))