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