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