[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/emacsql d5cfaee783 158/427: Be more precise about error me
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/emacsql d5cfaee783 158/427: Be more precise about error messages. |
Date: |
Tue, 13 Dec 2022 02:59:38 -0500 (EST) |
branch: elpa/emacsql
commit d5cfaee78389f35fd70b55bcf812b4c9e23f8bb4
Author: Christopher Wellons <wellons@nullprogram.com>
Commit: Christopher Wellons <wellons@nullprogram.com>
Be more precise about error messages.
---
emacsql-tests.el | 13 ++++
emacsql.el | 190 +++++++++++++++++++++++++++++++++++++++++--------------
2 files changed, 157 insertions(+), 46 deletions(-)
diff --git a/emacsql-tests.el b/emacsql-tests.el
index 3e109788db..36d78722ee 100644
--- a/emacsql-tests.el
+++ b/emacsql-tests.el
@@ -216,6 +216,19 @@
(should (equal (emacsql db [:select * :from likes])
'((1 yellow))))))
+(ert-deftest emacsql-error ()
+ "Check that we're getting expected conditions."
+ (should-error (emacsql-compile [:begin :foo])
+ :type 'emacsql-syntax)
+ (should-error (emacsql-compile [:create-table $foo$ [a]])
+ :type 'emacsql-syntax)
+ (should-error (emacsql-compile [:insert :into foo :values 1])
+ :type 'emacsql-syntax)
+ (emacsql-with-connection (db nil)
+ (emacsql db [:create-table foo [x]])
+ (should-error (emacsql db [:create-table foo [x]])
+ :type 'emacsql-table)))
+
(provide 'emacsql-tests)
;;; emacsql-tests.el ends here
diff --git a/emacsql.el b/emacsql.el
index abec8f6771..1e286709f5 100644
--- a/emacsql.el
+++ b/emacsql.el
@@ -79,6 +79,80 @@
nil)))
(error :cannot-execute)))))
+;;; Error definitions
+
+(defmacro emacsql-deferror (symbol parents message)
+ "Defines a new error symbol for Emacsql."
+ (declare (indent 2))
+ (let ((conditions (cl-remove-duplicates
+ (append parents (list symbol 'emacsql-error 'error)))))
+ `(prog1 ',symbol
+ (setf (get ',symbol 'error-conditions) ',conditions
+ (get ',symbol 'error-message) ,message))))
+
+(emacsql-deferror emacsql-error () ;; parent condition for all others
+ "Emacsql had an unhandled condition")
+
+(emacsql-deferror emacsql-syntax () "Invalid SQL statement")
+(emacsql-deferror emacsql-table () "SQL table error")
+(emacsql-deferror emacsql-lock () "Database locked")
+(emacsql-deferror emacsql-transaction () "Invalid transaction")
+(emacsql-deferror emacsql-fatal () "Fatal error")
+(emacsql-deferror emacsql-file () "Filesystem access error")
+
+(defvar emacsql-condition-alist
+ '(("unable to open" emacsql-file)
+ ("cannot open" emacsql-file)
+ ("source database is busy" emacsql-file)
+ ("unknown database" emacsql-file)
+ ("writable" emacsql-file)
+ ("no such table" emacsql-table)
+ ("table [^ ]+ already exists" emacsql-table)
+ ("no such column" emacsql-table)
+ ("already another table" emacsql-table)
+ ("Cannot add" emacsql-table)
+ ("table name" emacsql-table)
+ ("already an index" emacsql-table)
+ ("constraint cannot be drop" emacsql-table)
+ ("database is locked" emacsql-lock)
+ ("no transaction is active" emacsql-transaction)
+ ("cannot start a transaction" emacsql-transaction)
+ ("out of memory" emacsql-fatal)
+ ("corrupt database" emacsql-fatal)
+ ("interrupt" emacsql-fatal)
+ ("values were supplied" emacsql-syntax)
+ ("mismatch" emacsql-syntax)
+ ("no such" emacsql-syntax)
+ ("does not match" emacsql-syntax)
+ ("circularly defined" emacsql-syntax)
+ ("parameters are not allowed" emacsql-syntax)
+ ("missing" emacsql-syntax)
+ ("is only allowed on" emacsql-syntax)
+ ("more than one primary key" emacsql-syntax)
+ ("not constant" emacsql-syntax)
+ ("duplicate" emacsql-syntax)
+ ("name reserved" emacsql-syntax)
+ ("cannot use variables" emacsql-syntax)
+ ("no tables specified" emacsql-syntax)
+ ("syntax error" emacsql-syntax)
+ ("no such function" emacsql-syntax)
+ ("unknown function" emacsql-syntax)
+ ("wrong number of arguments" emacsql-syntax)
+ ("term does not match" emacsql-syntax)
+ ("clause" emacsql-syntax)
+ ("tree is too large" emacsql-syntax)
+ ("too many" emacsql-syntax))
+ "List of regexp's mapping sqlite3 output to conditions.")
+
+(defun emacsql-get-condition (message)
+ (or (cadr (cl-assoc message emacsql-condition-alist
+ :test (lambda (a b) (string-match-p b a))))
+ 'emacsql-error))
+
+(defun emacsql-error (format &rest args)
+ "Like `error', but signal an emacsql-syntax condition."
+ (signal 'emacsql-syntax (list (apply #'format format args))))
+
;;; Connection handling:
(cl-defstruct (emacsql (:constructor emacsql--create))
@@ -242,8 +316,10 @@ A statement can be a list, containing a statement with its
arguments."
(setf (point) (point-min))
(prog1 t
(when (looking-at "Error:")
- (error (buffer-substring (line-beginning-position)
- (line-end-position)))))))
+ (let* ((message (buffer-substring (line-beginning-position)
+ (line-end-position)))
+ (condition (emacsql-get-condition message)))
+ (signal condition (list message)))))))
;;; Escaping:
@@ -260,7 +336,7 @@ A statement can be a list, containing a statement with its
arguments."
(forbidden "[]-\000-\040!\"#%&'()*+,./;<=>?@[\\^`{|}~\177]"))
(when (or (string-match-p forbidden string)
(string-match-p "^[0-9$]" string))
- (error "Invalid Emacsql identifier: %S" identifier))
+ (emacsql-error "Invalid Emacsql identifier: %S" identifier))
(if (string-match-p ":" string)
(replace-regexp-in-string ":" "." string)
string)))
@@ -281,9 +357,10 @@ A statement can be a list, containing a statement with its
arguments."
(defun emacsql-escape-vector (vector)
"Encode VECTOR into a SQL vector scalar."
- (cl-etypecase vector
+ (cl-typecase vector
(list (mapconcat #'emacsql-escape-vector vector ", "))
- (vector (concat "(" (mapconcat #'emacsql-escape-value vector ", ") ")"))))
+ (vector (concat "(" (mapconcat #'emacsql-escape-value vector ", ") ")"))
+ (otherwise (emacsql-error "Invalid vector %S" vector))))
;; Structured SQL compilation:
@@ -329,7 +406,7 @@ a list of (<string> [arg-pos] ...)."
for (arity expander) = (emacsql-get-expander keyword)
when expander
collect (apply expander (cl-subseq items 0 arity)) into parts
- else do (error "Unrecognized keyword %s" keyword)
+ else do (emacsql-error "Unrecognized keyword %s" keyword)
do (setf items (cl-subseq items arity))
finally
(let ((string (concat (if subsql-p "(" "")
@@ -344,18 +421,20 @@ a list of (<string> [arg-pos] ...)."
"Fill in the variables EXPANSION with ARGS."
(cl-destructuring-bind (format . vars) expansion
(unless (= (length args) (length vars))
- (error "Wrong number of arguments for SQL template."))
+ (emacsql-error "Wrong number of arguments for SQL template."))
(apply #'format format
(cl-loop for (i . kind) in vars collect
(let ((thing (nth i args)))
- (cl-ecase kind
+ (cl-case kind
(:identifier (emacsql-escape-identifier thing))
(:value (emacsql-escape-value thing))
(:vector (emacsql-escape-vector thing))
(:schema (car (emacsql--schema-to-string thing)))
(:auto (if (symbolp thing)
(emacsql-escape-identifier thing)
- (emacsql-escape-value thing)))))))))
+ (emacsql-escape-value thing)))
+ (otherwise
+ (emacsql-error "Invalid var type %S" kind))))))))
(defun emacsql-compile (sql &rest args)
"Compile structured SQL expression into a string."
@@ -399,10 +478,11 @@ KIND should be :value or :identifier."
(when (and var (symbolp var)) (setf thing var))
(if (numberp var)
(prog1 "%s" (push (cons var kind) emacsql--vars))
- (cl-ecase kind
+ (cl-case kind
((:identifier :value :vector) (emacsql-escape-format thing kind))
(:auto (emacsql-escape-format
- thing (if (symbolp thing) :identifier :value)))))))
+ thing (if (symbolp thing) :identifier :value)))
+ (otherwise (emacsql-error "Invalid var type: %S" kind))))))
(defun emacsql--vars-combine (expanded)
"Only use within `emacsql-with-vars'!"
@@ -445,9 +525,9 @@ definitions for return from a `emacsql-defexpander'."
(object (setf type "TEXT"))
(otherwise
(if (keywordp next)
- (error "Unknown schema contraint %s" next)
- (error "Invalid type %s: %s" next
- "must be 'integer', 'float', or 'object'"))))))
+ (emacsql-error "Unknown schema contraint %s" next)
+ (emacsql-error "Invalid type %s: %s" next
+ "must be 'integer', 'float', or 'object'"))))))
(setf output (nreverse output))
(when type (push type output))
(push name output)
@@ -472,19 +552,21 @@ definitions for return from a `emacsql-defexpander'."
(format "(%s) REFERENCES %s (%s)" (idents child) (var table
:identifier)
(idents parent))
(cl-loop for (key value) on actions by #'cddr collect
- (cl-ecase key
+ (cl-case key
(:on-update "ON UPDATE")
- (:on-delete "ON DELETE"))
+ (:on-delete "ON DELETE")
+ (otherwise (emacsql-error "Invalid case: %S" key)))
collect
- (cl-ecase value
+ (cl-case value
(:restrict "RESTRICT")
(:set-nil "SET NULL")
(:set-default "SET DEFAULT")
- (:cascade "CASCADE"))))
+ (:cascade "CASCADE")
+ (otherwise (emacsql-error "Invalid action: %S" key)))))
" "))))
(defun emacsql--schema-to-string (schema)
- (cl-etypecase schema
+ (cl-typecase schema
(vector (emacsql--columns-to-string schema))
(list
(emacsql-with-vars ""
@@ -493,23 +575,27 @@ definitions for return from a `emacsql-defexpander'."
(cons
(combine (emacsql--columns-to-string (pop schema)))
(cl-loop for (key value) on schema by #'cddr collect
- (cl-ecase key
+ (cl-case key
(:primary (format "PRIMARY KEY (%s)" (idents value)))
(:unique (format "UNIQUE (%s)" (idents value)))
(:check (format "CHECK (%s)" (expr value)))
- (:foreign (combine (emacsql--foreign-key value))))))
- ", ")))))
+ (:foreign (combine (emacsql--foreign-key value)))
+ (otherwise
+ (emacsql-error "Invalid table constraint: %S" key)))))
+ ", ")))
+ (otherwise (emacsql-error "Invalid schema: %S" schema))))
(defun emacsql--vector (vector)
"Expand VECTOR, making variables as needed."
(emacsql-with-vars ""
- (cl-etypecase vector
+ (cl-typecase vector
(symbol
(var vector :vector))
(list
(mapconcat (lambda (v) (combine (emacsql--vector v))) vector ", "))
(vector
- (format "(%s)" (mapconcat (lambda (x) (var x :value)) vector ", "))))))
+ (format "(%s)" (mapconcat (lambda (x) (var x :value)) vector ", ")))
+ (otherwise (emacsql-error "Invalid vector: %S" vector)))))
(defun emacsql--expr (expr)
"Expand EXPR recursively."
@@ -518,16 +604,19 @@ definitions for return from a `emacsql-defexpander'."
((emacsql-sql-p expr) (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
+ (cl-flet ((recur (n) (combine (emacsql--expr (nth n args))))
+ (nops (op)
+ (emacsql-error "Wrong number of operands for %s" op)))
+ (cl-case op
;; Trinary/binary
((<= >=)
- (cl-ecase (length args)
+ (cl-case (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))))))
+ (recur (if (eq op '>=) 0 2))))
+ (otherwise (nops op))))
;; Binary
((< > = != like glob is * / % << >> + & | as)
(if (= 2 (length args))
@@ -535,17 +624,18 @@ definitions for return from a `emacsql-defexpander'."
(recur 0)
(if (eq op '%) '%% (upcase (symbol-name op)))
(recur 1))
- (error "Wrong number of operands for %s" op)))
+ (nops op)))
;; Unary
((not)
(if (= 1 (length args))
(format "%s %s" (upcase (symbol-name op)) (recur 0))
- (error "Wrong number of operands for %s" op)))
+ (nops op)))
;; Unary/Binary
((-)
- (cl-ecase (length args)
+ (cl-case (length args)
(1 (format "-(%s)" (recur 0)))
- (2 (format "%s - %s" (recur 0) (recur 1)))))
+ (2 (format "%s - %s" (recur 0) (recur 1)))
+ (otherwise (nops op))))
;; Variadic
((and or)
(cl-case (length args)
@@ -557,23 +647,26 @@ definitions for return from a `emacsql-defexpander'."
(format " %s " (upcase (symbol-name op)))))))
;; quote special case
((quote)
- (cl-ecase (length args)
- (1 (var (nth 0 args) :value))))
+ (cl-case (length args)
+ (1 (var (nth 0 args) :value))
+ (otherwise (nops op))))
;; IN special case
((in)
(cl-case (length args)
- (1 (error "Wrong number of operands for %s" op))
+ (1 (emacsql-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)))))))))))))
+ (format "%s IN %s" (recur 0) (subsql (cdr args))))))
+ (otherwise (emacsql-error "Unknown operator: %S" op)))))))))
(defun emacsql--idents (idents)
"Read in a vector of IDENTS identifiers, or just an single identifier."
(emacsql-with-vars ""
- (cl-etypecase idents
+ (cl-typecase idents
(symbol (var idents :identifier))
(list (expr idents))
- (vector (mapconcat (lambda (e) (expr e)) idents ", ")))))
+ (vector (mapconcat (lambda (e) (expr e)) idents ", "))
+ (otherwise (emacsql-error "Invalid syntax: %S" idents)))))
(defun emacsql-init-font-lock ()
"Add font-lock highlighting for `emacsql-defexpander'."
@@ -625,17 +718,21 @@ definitions for return from a `emacsql-defexpander'."
(emacsql-defexpander :order-by (columns)
(emacsql-with-vars "ORDER BY "
- (cl-flet ((order (k) (cl-ecase k (:asc " ASC") (:desc " DESC"))))
+ (cl-flet ((order (k) (cl-case k
+ (:asc " ASC")
+ (:desc " DESC")
+ (otherwise (emacsql-error "Invalid order: %S" k)))))
(if (not (vectorp columns))
(expr columns)
(cl-loop for column across columns collect
- (cl-etypecase column
+ (cl-typecase column
(list (let ((kpos (cl-position-if #'keywordp column)))
(if kpos
(concat (expr (nth (- 1 kpos) column))
(order (nth kpos column)))
(expr column))))
- (symbol (var column :identifier)))
+ (symbol (var column :identifier))
+ (otherwise (emacsql-error "Invalid order spec: %S" column)))
into parts
finally (cl-return (mapconcat #'identity parts ", ")))))))
@@ -680,9 +777,10 @@ definitions for return from a `emacsql-defexpander'."
(emacsql-defexpander :set (set)
(emacsql-with-vars "SET "
- (cl-etypecase set
+ (cl-typecase set
(vector (idents set))
- (list (expr set)))))
+ (list (expr set))
+ (otherwise (emacsql-error "Invalid SET expression: %S" set)))))
(emacsql-defexpander :union ()
(list "UNION"))
@@ -702,11 +800,12 @@ definitions for return from a `emacsql-defexpander'."
(emacsql-defexpander :begin (kind)
(emacsql-with-vars "BEGIN "
- (cl-ecase kind
+ (cl-case kind
(:transaction "TRANSACTION")
(:deferred "DEFERRED")
(:immediate "IMMEDIATE")
- (:exclusive "EXCLUSIVE"))))
+ (:exclusive "EXCLUSIVE")
+ (otherwise (emacsql-error "Unknown transaction type: %S" kind)))))
(emacsql-defexpander :commit ()
(list "COMMIT"))
@@ -724,7 +823,6 @@ definitions for return from a `emacsql-defexpander'."
(defun emacsql--indent ()
"Indent and wrap the SQL expression in the current buffer."
- (interactive)
(save-excursion
(setf (point) (point-min))
(let ((case-fold-search nil))
- [nongnu] elpa/emacsql 3012f5b725 154/427: Fix typo., (continued)
- [nongnu] elpa/emacsql 3012f5b725 154/427: Fix typo., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 301e2ff4b6 175/427: Drop "simple" from helper method names., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 578a71d2d8 157/427: Re-order some definitions to group them., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 935cca89a9 176/427: Add updated information about Windows., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql b3aea1e6af 055/427: Oops, fix >= back., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql c4396ec5e6 057/427: Fix a bunch of warnings., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql e9fbc4a913 061/427: Fill out a Makefile., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 829298d5a7 063/427: Add dependency listing., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 231ec586ca 088/427: Drop forced process coding system., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql eba26bead6 102/427: Add emacsql-compile function for debugging., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql d5cfaee783 158/427: Be more precise about error messages.,
ELPA Syncer <=
- [nongnu] elpa/emacsql f512300aba 054/427: Fix a few things in emacsql-expr., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql e900f99091 075/427: Update examples., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 1116a0873e 163/427: Turn everything into generic functions., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 47d9476a02 142/427: Add tests for quote operator., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql cd5e856ea6 150/427: Add :autoincrement., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql f31120edb6 155/427: Fix up documentation headers., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql adfbc7ed83 156/427: Add emacsql-show-last-sql., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 60d92d3051 159/427: Change the output mode to list., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql d2f1bcef5d 165/427: Rename add-connection to register., ELPA Syncer, 2022/12/13
- [nongnu] elpa/emacsql 9dcfebfba7 030/427: Factor out schema->string code., ELPA Syncer, 2022/12/13