branch: elpa/emacsql
commit 75ea77c9a0d1f5aede67a01dceeeaa3b077948fe
Author: Christopher Wellons <[email protected]>
Commit: Christopher Wellons <[email protected]>
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"))