1 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
6 ;;;; CLSQL square bracket symbolic query syntax. Functions for
7 ;;;; enabling and disabling the syntax and for building SQL
8 ;;;; expressions using the syntax.
10 ;;;; This file is part of CLSQL.
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 ;;;; *************************************************************************
17 (in-package #:clsql-sys)
19 (defvar *original-reader-enter* nil)
21 (defvar *original-reader-exit* nil)
23 (defvar *sql-macro-open-char* #\[)
25 (defvar *sql-macro-close-char* #\])
27 (defvar *restore-sql-reader-syntax* nil)
30 ;; Exported functions for disabling SQL syntax.
32 (defmacro disable-sql-reader-syntax ()
33 "Turns off the SQL reader syntax setting the syntax state such
34 that if the syntax is subsequently enabled,
35 RESTORE-SQL-READER-SYNTAX-STATE will disable it again."
36 '(eval-when (:compile-toplevel :load-toplevel :execute)
37 (setf *restore-sql-reader-syntax* nil)
38 (%disable-sql-reader-syntax)))
40 (defmacro locally-disable-sql-reader-syntax ()
41 "Turns off the SQL reader syntax without changing the syntax
42 state such that RESTORE-SQL-READER-SYNTAX-STATE will re-establish
43 the current syntax state."
44 '(eval-when (:compile-toplevel :load-toplevel :execute)
45 (%disable-sql-reader-syntax)))
47 (defun %disable-sql-reader-syntax ()
48 (set-macro-character *sql-macro-open-char* *original-reader-enter*)
49 (set-macro-character *sql-macro-close-char* *original-reader-exit*)
53 ;; Exported functions for enabling SQL syntax.
55 (defmacro enable-sql-reader-syntax ()
56 "Turns on the SQL reader syntax setting the syntax state such
57 that if the syntax is subsequently disabled,
58 RESTORE-SQL-READER-SYNTAX-STATE will enable it again."
59 '(eval-when (:compile-toplevel :load-toplevel :execute)
60 (setf *restore-sql-reader-syntax* t)
61 (%enable-sql-reader-syntax)))
63 (defmacro locally-enable-sql-reader-syntax ()
64 "Turns on the SQL reader syntax without changing the syntax
65 state such that RESTORE-SQL-READER-SYNTAX-STATE will re-establish
66 the current syntax state."
67 '(eval-when (:compile-toplevel :load-toplevel :execute)
68 (%enable-sql-reader-syntax)))
70 (defun %enable-sql-reader-syntax ()
71 (unless (eq (get-macro-character *sql-macro-open-char*) #'sql-reader-open)
72 (setf *original-reader-enter* (get-macro-character *sql-macro-open-char*))
73 (set-macro-character *sql-macro-open-char* #'sql-reader-open))
74 (unless (eq (get-macro-character *sql-macro-close-char*)
75 (get-macro-character #\)))
76 (setf *original-reader-exit* (get-macro-character *sql-macro-close-char*))
77 (set-macro-character *sql-macro-close-char* (get-macro-character #\))))
80 (defmacro restore-sql-reader-syntax-state ()
81 "Enables the SQL reader syntax if ENABLE-SQL-READER-SYNTAX has
82 been called more recently than DISABLE-SQL-READER-SYNTAX and
83 otherwise disables the SQL reader syntax. By default, the SQL
84 reader syntax is disabled."
85 '(eval-when (:compile-toplevel :load-toplevel :execute)
86 (if *restore-sql-reader-syntax*
87 (%enable-sql-reader-syntax)
88 (%disable-sql-reader-syntax))))
90 (defun sql-reader-open (stream char)
91 (declare (ignore char))
92 (let ((sqllist (read-delimited-list #\] stream t)))
93 (unless *read-suppress*
95 (cond ((string= (write-to-string (car sqllist)) "||")
96 (cons (sql-operator 'concat-op) (cdr sqllist)))
97 ((and (= (length sqllist) 1) (eql (car sqllist) '*))
98 (apply #'generate-sql-reference sqllist))
99 ((sql-operator (car sqllist))
100 (cons (sql-operator (car sqllist)) (cdr sqllist)))
101 (t (apply #'generate-sql-reference sqllist)))
103 (error 'sql-user-error
104 :message (format nil "Error ~A occured while attempting to parse '~A' at file position ~A"
105 (sql-user-error-message c) sqllist (file-position stream))))))))
107 (defun generate-sql-reference (&rest arglist)
108 (cond ((= (length arglist) 1) ; string, table or attribute
109 (if (stringp (car arglist))
110 (sql-expression :string (car arglist))
111 (sql-expression :attribute (car arglist))))
112 ((<= 2 (length arglist))
113 (let ((sqltype (when (keywordp (caddr arglist)) (caddr arglist) nil)))
115 ((stringp (cadr arglist))
116 (sql-expression :table (car arglist)
117 :alias (cadr arglist)
119 ((keywordp (cadr arglist))
120 (sql-expression :attribute (car arglist)
121 :type (cadr arglist)))
123 (sql-expression :attribute (cadr arglist)
127 (error 'sql-user-error :message "bad expression syntax"))))
130 ;; Exported functions for dealing with SQL syntax
132 (defun sql (&rest args)
133 "Returns an SQL string generated from the expressions ARGS. The
134 expressions are translated into SQL strings and then concatenated
135 with a single space delimiting each expression. An error of type
136 SQL-USER-ERROR is signalled if any element in ARGS is not of the
137 supported types (a symbol, string, number or symbolic SQL
138 expression) or a list or vector containing only these supported
140 (format nil "~{~A~^ ~}" (mapcar #'sql-output args)))
142 (defun sql-expression (&key string table alias attribute type)
143 "Returns an SQL expression constructed from the supplied
144 arguments which may be combined as follows: ATTRIBUTE and TYPE;
145 ATTRIBUTE; ALIAS or TABLE and ATTRIBUTE and TYPE; ALIAS or TABLE
146 and ATTRIBUTE; TABLE, ATTRIBUTE and TYPE; TABLE and ATTRIBUTE;
147 TABLE and ALIAS; TABLE; and STRING. An error of type
148 SQL-USER-ERROR is signalled if an unsupported combination of
149 keyword arguments is specified."
152 (make-instance 'sql :string string))
154 (make-instance 'sql-ident-attribute :name attribute
155 :qualifier (or table alias)
157 ((and table (not attribute))
158 (make-instance 'sql-ident-table :name table
159 :table-alias alias))))
161 (defun sql-operator (operator)
162 "Returns the Lisp symbol corresponding to the SQL operator
163 represented by the symbol OPERATOR. If OPERATOR does not
164 represent a supported SQL operator or is not a symbol, nil is
168 (symbol (values (gethash (symbol-name-default-case (symbol-name operator))
171 (defun sql-operation (operator &rest args)
172 "Returns an SQL expression constructed from the supplied symbol
173 OPERATOR representing an SQL operator or function and its
174 arguments ARGS. An error of type SQL-USER-ERROR is signalled if
175 OPERATOR is not a symbol representing a supported SQL
176 operator. If OPERATOR is passed the symbol FUNCTION then the
177 first value in ARGS must be a string representing a valid SQL
178 function and the remaining values in ARGS its arguments as
180 (if (sql-operator operator)
181 (apply (symbol-function (sql-operator operator)) args)
182 (error 'sql-user-error
184 (format nil "~A is not a recognized SQL operator." operator))))