branch: elpa/emacsql commit 75ea77c9a0d1f5aede67a01dceeeaa3b077948fe Author: Christopher Wellons <well...@nullprogram.com> Commit: Christopher Wellons <well...@nullprogram.com>
Add an "as" operator, greatly simplifying :from. --- README.md | 11 ++++-- emacsql-tests.el | 8 ++--- emacsql.el | 103 ++++++++++++++++++++++++------------------------------- 3 files changed, 57 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 8de9635404..6e85073a9a 100644 --- a/README.md +++ b/README.md @@ -103,13 +103,17 @@ so you'll need to enable it for each connection. ## Operators -Emacsql currently supports the following expression operators, named +Emacsql supports the following SQLite expression operators, named exactly like so in a structured Emacsql statement. * / % + - << >> & | < <= > >= = != is like glob and or in +In addition, Emacsql has these operators. + + quote as not + The `<=` and `>=` operators accept 2 or 3 operands, transforming into a SQL `_ BETWEEN _ AND _` operator as appropriate. @@ -180,6 +184,7 @@ column identifiers, optionally as expressions. ```el [:select [name (/ salary 52)] ...] +[:select [(as name n) (as age a)] ...] ``` #### :from `<table>` @@ -189,9 +194,9 @@ Provides `FROM`. ```el [... :from employees] [... :from [employees accounts]] -[... :from [employees (accounts a)]] +[... :from [employees (as accounts a)]] [... :from (:select ...)] -[... :from [((:select ...) s1) ((:select ...) s2)]] +[... :from [(as (:select ...) s1) (as (:select ...) s2)]] ``` #### :where `<expr>` diff --git a/emacsql-tests.el b/emacsql-tests.el index cb383f563f..763a014d0f 100644 --- a/emacsql-tests.el +++ b/emacsql-tests.el @@ -75,10 +75,10 @@ ;; Sub queries ([:select name :from (:select * :from $1)] '(people) "SELECT name FROM (SELECT * FROM people);") - ([:select name :from [people (accounts a)]] '() - "SELECT name FROM people, accounts a;") - ([:select p:name :from [((:select * :from people) p)]] '() - "SELECT p.name FROM (SELECT * FROM people) p;"))) + ([:select name :from [people (as accounts a)]] '() + "SELECT name FROM people, accounts AS a;") + ([:select p:name :from [(as (:select * :from people) p)]] '() + "SELECT p.name FROM (SELECT * FROM people) AS p;"))) (ert-deftest emacsql-create-table () (emacsql-tests-with-queries diff --git a/emacsql.el b/emacsql.el index 449544bba6..c79d90b70e 100644 --- a/emacsql.el +++ b/emacsql.el @@ -504,54 +504,56 @@ definitions for return from a `emacsql-defexpander'." (defun emacsql--expr (expr) "Expand EXPR recursively." (emacsql-with-vars "" - (if (atom expr) - (var expr :auto) - (cl-destructuring-bind (op . args) expr - (cl-flet ((recur (n) (combine (emacsql--expr (nth n args))))) - (cl-ecase op - ;; Trinary/binary - ((<= >=) - (cl-ecase (length args) - (2 (format "%s %s %s" (recur 0) op (recur 1))) - (3 (format "%s BETWEEN %s AND %s" - (recur 1) - (recur (if (eq op '>=) 2 0)) - (recur (if (eq op '>=) 0 2)))))) - ;; Binary - ((< > = != like glob is and or * / % << >> + & |) - (if (= 2 (length args)) - (format "%s %s %s" - (recur 0) - (if (eq op '%) '%% (upcase (symbol-name op))) - (recur 1)) - (error "Wrong number of operands for %s" op))) - ;; Unary - ((not) - (if (= 1 (length args)) - (format "%s %s" (upcase (symbol-name op)) (recur 0)) - (error "Wrong number of operands for %s" op))) - ;; Unary/Binary - ((-) - (cl-ecase (length args) - (1 (format "-(%s)" (recur 0))) - (2 (format "%s - %s" (recur 0) (recur 1))))) - ;; quote special case - ((quote) - (cl-ecase (length args) - (1 (var (nth 0 args) :value)))) - ;; IN special case - ((in) - (cl-case (length args) - (1 (error "Wrong number of operands for %s" op)) - (2 (format "%s IN %s" (recur 0) (var (nth 1 args) :vector))) - (otherwise - (format "%s IN %s" (recur 0) (subsql (cdr args)))))))))))) + (cond + ((and (sequencep expr) (eq :select (elt expr 0))) (subsql expr)) + ((atom expr) (var expr :auto)) + ((cl-destructuring-bind (op . args) expr + (cl-flet ((recur (n) (combine (emacsql--expr (nth n args))))) + (cl-ecase op + ;; Trinary/binary + ((<= >=) + (cl-ecase (length args) + (2 (format "%s %s %s" (recur 0) op (recur 1))) + (3 (format "%s BETWEEN %s AND %s" + (recur 1) + (recur (if (eq op '>=) 2 0)) + (recur (if (eq op '>=) 0 2)))))) + ;; Binary + ((< > = != like glob is and or * / % << >> + & | as) + (if (= 2 (length args)) + (format "%s %s %s" + (recur 0) + (if (eq op '%) '%% (upcase (symbol-name op))) + (recur 1)) + (error "Wrong number of operands for %s" op))) + ;; Unary + ((not) + (if (= 1 (length args)) + (format "%s %s" (upcase (symbol-name op)) (recur 0)) + (error "Wrong number of operands for %s" op))) + ;; Unary/Binary + ((-) + (cl-ecase (length args) + (1 (format "-(%s)" (recur 0))) + (2 (format "%s - %s" (recur 0) (recur 1))))) + ;; quote special case + ((quote) + (cl-ecase (length args) + (1 (var (nth 0 args) :value)))) + ;; IN special case + ((in) + (cl-case (length args) + (1 (error "Wrong number of operands for %s" op)) + (2 (format "%s IN %s" (recur 0) (var (nth 1 args) :vector))) + (otherwise + (format "%s IN %s" (recur 0) (subsql (cdr args))))))))))))) (defun emacsql--idents (idents) "Read in a vector of IDENTS identifiers, or just an single identifier." (emacsql-with-vars "" (cl-etypecase idents (symbol (var idents :identifier)) + (list (expr idents)) (vector (mapconcat (lambda (e) (expr e)) idents ", "))))) (defun emacsql-init-font-lock () @@ -573,22 +575,7 @@ definitions for return from a `emacsql-defexpander'." (emacsql-defexpander :from (sources) "Expands to the FROM keyword." (emacsql-with-vars "FROM " - (cl-etypecase sources - (symbol (var sources :identifier)) - (list (if (eq :select (car sources)) - (subsql sources) - (cl-destructuring-bind (table alias) sources - (concat (var table :identifier) " " (var alias :identifier))))) - (vector (mapconcat (lambda (source) - (cl-etypecase source - (symbol (var source :identifier)) - (list - (cl-destructuring-bind (table alias) source - (concat (if (symbolp table) - (var table :identifier) - (subsql table)) - " " (var alias :identifier)))))) - sources ", "))))) + (idents sources))) (emacsql-defexpander :replace () (list "REPLACE"))