1 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
4 ;;;; CLSQL square bracket symbolic query syntax. Functions for
5 ;;;; enabling and disabling the syntax and for building SQL
6 ;;;; expressions using the syntax.
8 ;;;; This file is part of CLSQL.
10 ;;;; CLSQL users are granted the rights to distribute and use this software
11 ;;;; as governed by the terms of the Lisp Lesser GNU Public License
12 ;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
13 ;;;; *************************************************************************
15 (in-package #:clsql-sys)
17 (defvar *original-readtable* nil)
19 (defvar *sql-macro-open-char* #\[)
21 (defvar *sql-macro-close-char* #\])
23 (defvar *restore-sql-reader-syntax* nil)
26 ;; Exported functions for disabling SQL syntax.
28 (defmacro disable-sql-reader-syntax ()
29 "Turns off the SQL reader syntax setting the syntax state such
30 that if the syntax is subsequently enabled,
31 RESTORE-SQL-READER-SYNTAX-STATE will disable it again."
32 '(eval-when (:compile-toplevel :load-toplevel :execute)
33 (setf *restore-sql-reader-syntax* nil)
34 (%disable-sql-reader-syntax)))
36 (defmacro locally-disable-sql-reader-syntax ()
37 "Turns off the SQL reader syntax without changing the syntax
38 state such that RESTORE-SQL-READER-SYNTAX-STATE will re-establish
39 the current syntax state."
40 '(eval-when (:compile-toplevel :load-toplevel :execute)
41 (%disable-sql-reader-syntax)))
43 (defun %disable-sql-reader-syntax ()
44 (when *original-readtable*
45 (setf *readtable* *original-readtable*
46 *original-readtable* nil))
50 ;; Exported functions for enabling SQL syntax.
52 (defmacro enable-sql-reader-syntax ()
53 "Turns on the SQL reader syntax setting the syntax state such
54 that if the syntax is subsequently disabled,
55 RESTORE-SQL-READER-SYNTAX-STATE will enable it again."
56 '(eval-when (:compile-toplevel :load-toplevel :execute)
57 (setf *restore-sql-reader-syntax* t)
58 (%enable-sql-reader-syntax)))
60 (defmacro locally-enable-sql-reader-syntax ()
61 "Turns on the SQL reader syntax without changing the syntax
62 state such that RESTORE-SQL-READER-SYNTAX-STATE will re-establish
63 the current syntax state."
64 '(eval-when (:compile-toplevel :load-toplevel :execute)
65 (%enable-sql-reader-syntax)))
67 (defun %enable-sql-reader-syntax ()
68 (unless *original-readtable*
69 (setf *original-readtable* *readtable*
70 *readtable* (copy-readtable))
71 (set-macro-character *sql-macro-open-char* #'sql-reader-open)
72 (set-macro-character *sql-macro-close-char* (get-macro-character #\))))
75 (defmacro restore-sql-reader-syntax-state ()
76 "Enables the SQL reader syntax if ENABLE-SQL-READER-SYNTAX has
77 been called more recently than DISABLE-SQL-READER-SYNTAX and
78 otherwise disables the SQL reader syntax. By default, the SQL
79 reader syntax is disabled."
80 '(eval-when (:compile-toplevel :load-toplevel :execute)
81 (if *restore-sql-reader-syntax*
82 (%enable-sql-reader-syntax)
83 (%disable-sql-reader-syntax))))
85 (defun sql-reader-open (stream char)
86 (declare (ignore char))
87 (let ((sqllist (read-delimited-list #\] stream t)))
88 (unless *read-suppress*
90 (cond ((string= (write-to-string (car sqllist)) "||")
91 (cons (sql-operator 'concat-op) (cdr sqllist)))
92 ((and (= (length sqllist) 1) (eql (car sqllist) '*))
93 (apply #'generate-sql-reference sqllist))
94 ((sql-operator (car sqllist))
95 (cons (sql-operator (car sqllist)) (cdr sqllist)))
96 (t (apply #'generate-sql-reference sqllist)))
98 (error 'sql-user-error
99 :message (format nil "Error ~A occured while attempting to parse '~A' at file position ~A"
100 (sql-user-error-message c) sqllist (file-position stream))))))))
102 (defun generate-sql-reference (&rest arglist)
103 (cond ((= (length arglist) 1) ; string, table or attribute
104 (if (stringp (car arglist))
105 (sql-expression :string (car arglist))
106 (sql-expression :attribute (car arglist))))
107 ((<= 2 (length arglist))
108 (let ((sqltype (when (keywordp (caddr arglist)) (caddr arglist) nil)))
110 ((stringp (cadr arglist))
111 (sql-expression :table (car arglist)
112 :alias (cadr arglist)
114 ((keywordp (cadr arglist))
115 (sql-expression :attribute (car arglist)
116 :type (cadr arglist)))
118 (sql-expression :attribute (cadr arglist)
122 (error 'sql-user-error :message "bad expression syntax"))))
125 ;; Exported functions for dealing with SQL syntax
127 (defun sql (&rest args)
128 "Returns an SQL string generated from the expressions ARGS. The
129 expressions are translated into SQL strings and then concatenated
130 with a single space delimiting each expression. An error of type
131 SQL-USER-ERROR is signalled if any element in ARGS is not of the
132 supported types (a symbol, string, number or symbolic SQL
133 expression) or a list or vector containing only these supported
135 (format nil "~{~A~^ ~}" (mapcar #'sql-output args)))
137 (defun sql-expression (&key string table alias attribute type)
138 "Returns an SQL expression constructed from the supplied
139 arguments which may be combined as follows: ATTRIBUTE and TYPE;
140 ATTRIBUTE; ALIAS or TABLE and ATTRIBUTE and TYPE; ALIAS or TABLE
141 and ATTRIBUTE; TABLE, ATTRIBUTE and TYPE; TABLE and ATTRIBUTE;
142 TABLE and ALIAS; TABLE; and STRING. An error of type
143 SQL-USER-ERROR is signalled if an unsupported combination of
144 keyword arguments is specified."
147 (make-instance 'sql :string string))
149 (make-instance 'sql-ident-attribute :name attribute
150 :qualifier (or table alias)
152 ((and table (not attribute))
153 (make-instance 'sql-ident-table :name table
154 :table-alias alias))))
156 (defun sql-operator (operator)
157 "Returns the Lisp symbol corresponding to the SQL operator
158 represented by the symbol OPERATOR. If OPERATOR does not
159 represent a supported SQL operator or is not a symbol, nil is
163 (symbol (values (gethash (symbol-name-default-case (symbol-name operator))
166 (defun sql-operation (operator &rest args)
167 "Returns an SQL expression constructed from the supplied symbol
168 OPERATOR representing an SQL operator or function and its
169 arguments ARGS. An error of type SQL-USER-ERROR is signalled if
170 OPERATOR is not a symbol representing a supported SQL
171 operator. If OPERATOR is passed the symbol FUNCTION then the
172 first value in ARGS must be a string representing a valid SQL
173 function and the remaining values in ARGS its arguments as
175 (if (sql-operator operator)
176 (apply (symbol-function (sql-operator operator)) args)
177 (error 'sql-user-error
179 (format nil "~A is not a recognized SQL operator." operator))))