(in-package #:clsql-sys)
-(defvar *original-reader-enter* nil)
-
-(defvar *original-reader-exit* nil)
+(defvar *original-readtable* nil)
(defvar *sql-macro-open-char* #\[)
(%disable-sql-reader-syntax)))
(defun %disable-sql-reader-syntax ()
- (when *original-reader-enter*
- (set-macro-character *sql-macro-open-char* *original-reader-enter*))
- (setf *original-reader-enter* nil)
+ (when *original-readtable*
+ (setf *readtable* *original-readtable*
+ *original-readtable* nil))
(values))
(%enable-sql-reader-syntax)))
(defun %enable-sql-reader-syntax ()
- (unless *original-reader-enter*
- (setf *original-reader-enter* (get-macro-character *sql-macro-open-char*)))
- (set-macro-character *sql-macro-open-char* #'sql-reader-open)
- (enable-sql-close-syntax)
+ (unless *original-readtable*
+ (setf *original-readtable* *readtable*
+ *readtable* (copy-readtable))
+ (set-macro-character *sql-macro-open-char* #'sql-reader-open)
+ (set-macro-character *sql-macro-close-char* (get-macro-character #\))))
(values))
(defmacro restore-sql-reader-syntax-state ()
(defun sql-reader-open (stream char)
(declare (ignore char))
(let ((sqllist (read-delimited-list #\] stream t)))
- (cond ((string= (write-to-string (car sqllist)) "||")
- (cons (sql-operator 'concat) (cdr sqllist)))
- ((and (= (length sqllist) 1) (eql (car sqllist) '*))
- (apply #'generate-sql-reference sqllist))
- ((sql-operator (car sqllist))
- (cons (sql-operator (car sqllist)) (cdr sqllist)))
- (t (apply #'generate-sql-reference sqllist)))))
-
-(defun disable-sql-close-syntax ()
- "Internal function that disables the close syntax when leaving
- sql context."
- (set-macro-character *sql-macro-close-char* *original-reader-exit*)
- (setf *original-reader-exit* nil))
-
-(defun enable-sql-close-syntax ()
- "Internal function that enables close syntax when entering SQL
- context."
- (setf *original-reader-exit* (get-macro-character *sql-macro-close-char*))
- (set-macro-character *sql-macro-close-char* (get-macro-character #\))))
+ (unless *read-suppress*
+ (handler-case
+ (cond ((string= (write-to-string (car sqllist)) "||")
+ (cons (sql-operator 'concat-op) (cdr sqllist)))
+ ((and (= (length sqllist) 1) (eql (car sqllist) '*))
+ (apply #'generate-sql-reference sqllist))
+ ((sql-operator (car sqllist))
+ (cons (sql-operator (car sqllist)) (cdr sqllist)))
+ (t (apply #'generate-sql-reference sqllist)))
+ (sql-user-error (c)
+ (error 'sql-user-error
+ :message (format nil "Error ~A occured while attempting to parse '~A' at file position ~A"
+ (sql-user-error-message c) sqllist (file-position stream))))))))
(defun generate-sql-reference (&rest arglist)
- (cond ((= (length arglist) 1) ; string, table or attribute
- (if (stringp (car arglist))
- (sql-expression :string (car arglist))
+ (cond ((= (length arglist) 1) ; string, table or attribute
+ (if (stringp (car arglist))
+ (sql-expression :string (car arglist))
(sql-expression :attribute (car arglist))))
- ((<= 2 (length arglist))
- (let ((sqltype (when (keywordp (caddr arglist)) (caddr arglist) nil)))
+ ((<= 2 (length arglist))
+ (let ((sqltype (when (keywordp (caddr arglist)) (caddr arglist) nil)))
(cond
((stringp (cadr arglist))
- (sql-expression :table (car arglist)
- :alias (cadr arglist)
- :type sqltype))
- ((keywordp (cadr arglist))
- (sql-expression :attribute (car arglist)
- :type (cadr arglist)))
- (t
- (sql-expression :attribute (cadr arglist)
- :table (car arglist)
- :type sqltype)))))
- (t
- (error 'sql-user-error :message "bad expression syntax"))))
-
-
-;; Exported functions for dealing with SQL syntax
+ (sql-expression :table (car arglist)
+ :alias (cadr arglist)
+ :type sqltype))
+ ((keywordp (cadr arglist))
+ (sql-expression :attribute (car arglist)
+ :type (cadr arglist)))
+ (t
+ (sql-expression :attribute (cadr arglist)
+ :table (car arglist)
+ :type sqltype)))))
+ (t
+ (error 'sql-user-error :message "bad expression syntax"))))
+
+
+;; Exported functions for dealing with SQL syntax
(defun sql (&rest args)
- "Returns an SQL string generated from the SQL expressions
-ARGS. The expressions are translated into SQL strings and then
-concatenated with a single space delimiting each expression."
+ "Returns an SQL string generated from the expressions ARGS. The
+expressions are translated into SQL strings and then concatenated
+with a single space delimiting each expression. An error of type
+SQL-USER-ERROR is signalled if any element in ARGS is not of the
+supported types (a symbol, string, number or symbolic SQL
+expression) or a list or vector containing only these supported
+types."
(format nil "~{~A~^ ~}" (mapcar #'sql-output args)))
(defun sql-expression (&key string table alias attribute type)
- "Returns an SQL expression constructed from the supplied arguments
-which may be combined as follows: ATTRIBUTE and TYPE; ATTRIBUTE;
-ALIAS or TABLE and ATTRIBUTE and TYPE; ALIAS or TABLE and
-ATTRIBUTE; TABLE, ATTRIBUTE and TYPE; TABLE and ATTRIBUTE; TABLE
-and ALIAS; TABLE; and STRING."
+ "Returns an SQL expression constructed from the supplied
+arguments which may be combined as follows: ATTRIBUTE and TYPE;
+ATTRIBUTE; ALIAS or TABLE and ATTRIBUTE and TYPE; ALIAS or TABLE
+and ATTRIBUTE; TABLE, ATTRIBUTE and TYPE; TABLE and ATTRIBUTE;
+TABLE and ALIAS; TABLE; and STRING. An error of type
+SQL-USER-ERROR is signalled if an unsupported combination of
+keyword arguments is specified."
(cond
(string
(make-instance 'sql :string string))
(make-instance 'sql-ident-table :name table
:table-alias alias))))
-(defun sql-operator (operation)
- "Returns the Lisp symbol corresponding to the SQL operation
- represented by the symbol OPERATION."
- (typecase operation
+(defun sql-operator (operator)
+ "Returns the Lisp symbol corresponding to the SQL operator
+ represented by the symbol OPERATOR. If OPERATOR does not
+ represent a supported SQL operator or is not a symbol, nil is
+ returned."
+ (typecase operator
(string nil)
- (symbol (gethash (symbol-name-default-case (symbol-name operation))
- *sql-op-table*))))
-
-(defun sql-operation (operation &rest rest)
- "Returns an SQL expression constructed from the supplied SQL
-operator or function OPERATION and its arguments REST. If
-OPERATION is passed the symbol FUNCTION then the first value in
-REST is taken to be a valid SQL function and the remaining values
-in REST its arguments."
- (if (sql-operator operation)
- (apply (symbol-function (sql-operator operation)) rest)
- (error "~A is not a recognized SQL operator." operation)))
+ (symbol (values (gethash (symbol-name-default-case (symbol-name operator))
+ *sql-op-table*)))))
+
+(defun sql-operation (operator &rest args)
+ "Returns an SQL expression constructed from the supplied symbol
+OPERATOR representing an SQL operator or function and its
+arguments ARGS. An error of type SQL-USER-ERROR is signalled if
+OPERATOR is not a symbol representing a supported SQL
+operator. If OPERATOR is passed the symbol FUNCTION then the
+first value in ARGS must be a string representing a valid SQL
+function and the remaining values in ARGS its arguments as
+strings."
+ (if (sql-operator operator)
+ (apply (symbol-function (sql-operator operator)) args)
+ (error 'sql-user-error
+ :message
+ (format nil "~A is not a recognized SQL operator." operator))))