[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/emacsql 896bb7ce52 035/427: Start adding SQL expanders.
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/emacsql 896bb7ce52 035/427: Start adding SQL expanders. |
Date: |
Tue, 13 Dec 2022 02:59:25 -0500 (EST) |
branch: elpa/emacsql
commit 896bb7ce52c25b2253963579787102a8cc3ff9da
Author: Christopher Wellons <wellons@nullprogram.com>
Commit: Christopher Wellons <wellons@nullprogram.com>
Start adding SQL expanders.
---
emacsql.el | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 85 insertions(+)
diff --git a/emacsql.el b/emacsql.el
index 9badf8cd6e..07a2b97901 100644
--- a/emacsql.el
+++ b/emacsql.el
@@ -292,6 +292,91 @@ Each row must be a sequence of values to store into TABLE.
(emacsql--check-error conn)
(emacsql--parse conn))
+;; SQL Expansion Functions
+
+(defvar emacsql-expanders ()
+ "Alist of all expansion functions.")
+
+(defun emacsql-add-expander (keyword function)
+ "Register FUNCTION for KEYWORD as a SQL expander.
+FUNCTION should accept a single argument, the keyword's argument,
+and should return a list of (<string> [arg-pos] ...)."
+ (push (cons keyword function) emacsql-expanders)
+ :keyword)
+
+(defmacro emacsql-defexpander (keyword args &rest body)
+ "Define an expander for KEYWORD."
+ (declare (indent 2))
+ `(emacsql-add-expander ,keyword (lambda ,args ,@body)))
+
+(defun emacsql-expand (sql)
+ "Expand SQL into a SQL-consumable string, with variables."
+ (loop for (keyword argument) on (cl-coerce sql 'list) by #'cddr
+ for expander = (cdr (assoc keyword emacsql-expanders))
+ when expander collect (funcall expander argument) into parts
+ else do (error "Unrecognized keyword %s" keyword)
+ finally (return (cons (concat (mapconcat #'car parts " ") ";")
+ (apply #'nconc (mapcar #'cdr parts))))))
+
+(defun emacsql-format (expansion &rest args)
+ "Fill in the variables EXPANSION with ARGS."
+ (cl-destructuring-bind (format . vars) expansion
+ (apply #'format format
+ (cl-loop for (i . kind) in vars collect
+ (cl-ecase kind
+ (:identifier (emacsql-escape (nth i args)))
+ (:value (emacsql-escape-value (nth i args))))))))
+
+(defun emacsql-var (var)
+ "Return the index number of VAR, or nil if VAR is not a variable.
+A variable is a symbol that looks like $1, $2, $3, etc. A $ means $1."
+ (when (symbolp var)
+ (let ((name (symbol-name var)))
+ (when (eql (aref name 0) ?$)
+ (if (> (length name) 1)
+ (1- (read (substring name 1)))
+ 0)))))
+
+(defun emacsql-escape-format (thing &optional kind)
+ "Escape THING for use as a `format' spec, pre-escaping for KIND.
+KIND should be :value or :identifier."
+ (replace-regexp-in-string
+ "%" "%%" (case kind
+ (:value (emacsql-escape-value thing))
+ (:identifier (emacsql-escape thing))
+ (otherwise thing))))
+
+(emacsql-defexpander :select (arg)
+ "Expands to the SELECT keyword."
+ (let ((vars ()))
+ (cons
+ (concat
+ "SELECT "
+ (cond
+ ((eq '* arg) "*")
+ ((emacsql-var arg)
+ (push (cons (emacsql-var arg) :identifier) vars)
+ "%s")
+ ((symbolp arg)
+ (emacsql-escape-format arg :identifier))
+ ((vectorp arg)
+ (mapconcat
+ #'identity
+ (cl-loop for name elements of arg
+ when (emacsql-var name)
+ collect (prog1 "%s" (push (cons it :identifier) vars))
+ else when (symbolp name)
+ collect (emacsql-escape-format name :identifier)
+ else do (error "Unknown format %S" name))
+ ", "))))
+ (nreverse vars))))
+
+(emacsql-defexpander :from (table)
+ "Expands to the FROM keyword."
+ (if (emacsql-var table)
+ (list "FROM %s" (cons (emacsql-var table) :identifier))
+ (list (concat "FROM " (emacsql-escape-format table :identifier)))))
+
(provide 'emacsql)
;;; emacsql.el ends here
- [nongnu] elpa/emacsql a6dbb52a3c 009/427: Add flatten option., (continued)
- [nongnu] elpa/emacsql a6dbb52a3c 009/427: Add flatten option., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 2053a15b21 010/427: Add Emacs requirement note., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 961c689269 011/427: Rename information to constraints., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql c7a8ef0908 015/427: Rename every emacsql argument to conn., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql d083cbb70e 017/427: Add in-memory database support., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 9091fe8df5 019/427: Rename sqlite-program-name into namespace., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 9c3293c57e 020/427: Add a docstring to emacsql struct., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 178a31d5ca 024/427: Lock in the coding system., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 85076dfe0f 027/427: Flesh out example more., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql c9854331c0 028/427: Add (un)license header., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 896bb7ce52 035/427: Start adding SQL expanders.,
ELPA Syncer <=
- [nongnu] elpa/emacsql 9272d13ace 036/427: Add some tests for the expanders., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 3511a0df1d 037/427: Add a top-level query function, emacsql., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 7e18a43da1 039/427: Add a template example., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 8b2cf8c208 041/427: Fix up :where., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql c92c56ce55 003/427: Add a close buffer sentinel., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 1a84c983cf 013/427: No named results by default., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 76a430032e 014/427: Fix up old test., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 36c6aae4b5 042/427: Drop raw select., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 77ae713186 046/427: Create table expander., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql e859204451 064/427: Flesh out more README., ELPA Syncer, 2022/12/13