r9796: * sql/expressions.lisp: reactivate caching of generated SQL
authorMarcus Pearce <m.t.pearce@city.ac.uk>
Fri, 16 Jul 2004 12:00:15 +0000 (12:00 +0000)
committerMarcus Pearce <m.t.pearce@city.ac.uk>
Fri, 16 Jul 2004 12:00:15 +0000 (12:00 +0000)
        strings.
        Move methods for DATABASE-OUTPUT-SQL, OUTPUT-SQL and SQL-HASH-KEY
        here from sql/fdml.lisp. Rationalise behaviour of SQL-OUTPUT,
        OUTPUT-SQL and DATABASE-OUTPUT-SQL.
        * sql/fdml.lisp: remove disabled method ADD-STORAGE-CLASS. Move
        methods for DATABASE-OUTPUT-SQL, OUTPUT-SQL and SQL-HASH-KEY to
        sql/expressions.lisp.
        * sql/ooddl.lisp: replace call to DATABASE-OUTPUT-SQL in
        DATABASE-PKEY-CONSTRAINT with call to SQL-OUTPUT.
        * sql/generics.lisp: add docstrings.

ChangeLog
sql/expressions.lisp
sql/fdml.lisp
sql/generics.lisp
sql/ooddl.lisp

index 42dc56a9cac5626e221b32ce81d81aec1e864aac..fe3f1d2bf1a99a6e1d21770b5ee9e8ffea2a4513 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+16 Jul 2004 Marcus Pearce <m.t.pearce@city.ac.uk> 
+       * sql/expressions.lisp: reactivate caching of generated SQL strings. 
+       Move methods for DATABASE-OUTPUT-SQL, OUTPUT-SQL and SQL-HASH-KEY 
+       here from sql/fdml.lisp. Rationalise behaviour of SQL-OUTPUT, 
+       OUTPUT-SQL and DATABASE-OUTPUT-SQL. 
+       * sql/fdml.lisp: remove disabled method ADD-STORAGE-CLASS. Move 
+       methods for DATABASE-OUTPUT-SQL, OUTPUT-SQL and SQL-HASH-KEY to 
+       sql/expressions.lisp. 
+       * sql/ooddl.lisp: replace call to DATABASE-OUTPUT-SQL in 
+       DATABASE-PKEY-CONSTRAINT with call to SQL-OUTPUT. 
+       * sql/generics.lisp: add docstrings. 
+
 15 Jul 2004 Kevin Rosenberg <kevin@rosenberg.net>
        * Version 2.11.16
        * db-oracle/oracle-sql.lisp: Remove OpenMCL specific
index 2a0085f6d3f4dd53f6f7122ef51f418f2b81a894..2f40e33a6e67f640b107a0a2dc9d444941c9287d 100644 (file)
   "stream which accumulates SQL output")
 
 (defun sql-output (sql-expr &optional database)
+  "Top-level call for generating SQL strings. Returns an SQL
+  string appropriate for DATABASE which corresponds to the
+  supplied lisp expression SQL-EXPR."
   (progv '(*sql-stream*)
       `(,(make-string-output-stream))
     (output-sql sql-expr database)
     (get-output-stream-string *sql-stream*)))
 
+(defmethod output-sql (expr database)
+  (write-string (database-output-sql expr database) *sql-stream*)
+  (values))
+
+(defvar *output-hash* (make-hash-table :test #'equal)
+  "For caching generated SQL strings.")
+
+(defmethod output-sql :around ((sql t) database)
+  (let* ((hash-key (output-sql-hash-key sql database))
+         (hash-value (when hash-key (gethash hash-key *output-hash*))))
+    (cond ((and hash-key hash-value)
+           (write-string hash-value *sql-stream*))
+          (hash-key
+           (let ((*sql-stream* (make-string-output-stream)))
+             (call-next-method)
+             (setf hash-value (get-output-stream-string *sql-stream*))
+             (setf (gethash hash-key *output-hash*) hash-value))
+           (write-string hash-value *sql-stream*))
+          (t
+           (call-next-method)))))
+
+(defmethod output-sql-hash-key (expr database)
+  (declare (ignore expr database))
+  nil)
+
 
 (defclass %sql-expression ()
   ())
           (sql-output ident nil)))
 
 ;; For SQL Identifiers of generic type
+
 (defclass sql-ident (%sql-expression)
   ((name
     :initarg :name
-    :initform "NULL"))
+    :initform +null-string+))
   (:documentation "An SQL identifer."))
 
 (defmethod make-load-form ((sql sql-ident) &optional environment)
     sql
     `(make-instance 'sql-ident :name ',name)))
 
-(defvar *output-hash* (make-hash-table :test #'equal))
-
-(defmethod output-sql-hash-key (expr database)
-  (declare (ignore expr database))
-  nil)
-
-#+ignore
-(defmethod output-sql :around ((sql t) database)
-  (let* ((hash-key (output-sql-hash-key sql database))
-         (hash-value (when hash-key (gethash hash-key *output-hash*))))
-    (cond ((and hash-key hash-value)
-           (write-string hash-value *sql-stream*))
-          (hash-key
-           (let ((*sql-stream* (make-string-output-stream)))
-             (call-next-method)
-             (setf hash-value (get-output-stream-string *sql-stream*))
-             (setf (gethash hash-key *output-hash*) hash-value))
-           (write-string hash-value *sql-stream*))
-          (t
-           (call-next-method)))))
-
 (defmethod output-sql ((expr sql-ident) database)
   (with-slots (name) expr
     (write-string
 (defclass sql-ident-attribute (sql-ident)
   ((qualifier
     :initarg :qualifier
-    :initform "NULL")
+    :initform +null-string+)
    (type
     :initarg :type
-    :initform "NULL"))
+    :initform +null-string+))
   (:documentation "An SQL Attribute identifier."))
 
 (defmethod collect-table-refs (sql)
     t))
 
 (defmethod output-sql-hash-key ((expr sql-ident-attribute) database)
-  (declare (ignore database))
   (with-slots (qualifier name type)
-    expr
-    (list 'sql-ident-attribute qualifier name type)))
+      expr
+    (list (and database (database-underlying-type database))
+          'sql-ident-attribute qualifier name type)))
 
 ;; For SQL Identifiers for tables
+
 (defclass sql-ident-table (sql-ident)
   ((alias
     :initarg :table-alias :initform nil))
           (format *sql-stream* "~s" alias))))
   t)
 
-#|
-(defmethod database-output-sql ((self duration) database)
-  (declare (ignore database))
-  (format nil "'~a'" (duration-timestring self)))
-
-(defmethod database-output-sql ((self money) database)
-  (database-output-sql (slot-value self 'odcl::units) database))
-|#
-
-
 (defmethod output-sql-hash-key ((expr sql-ident-table) database)
-  (declare (ignore database))
   (with-slots (name alias)
-    expr
-    (list 'sql-ident-table name alias)))
+      expr
+    (list (and database (database-underlying-type database))
+          'sql-ident-table name alias)))
 
 (defclass sql-relational-exp (%sql-expression)
   ((operator
   ()
   (:documentation "An SQL 'like' that upcases its arguments."))
   
-;; Write SQL for relational operators (like 'AND' and 'OR').
-;; should do arity checking of subexpressions
-  
 (defmethod output-sql ((expr sql-upcase-like) database)
   (flet ((write-term (term)
            (write-string "upper(" *sql-stream*)
   (:documentation "An SQL typecast expression."))
 
 (defmethod output-sql ((expr sql-typecast-exp) database)
-  (database-output-sql expr database))
-
-(defmethod database-output-sql ((expr sql-typecast-exp) database)
   (with-slots (components)
     expr
     (output-sql components database)))
 
-
 (defmethod collect-table-refs ((sql sql-typecast-exp))
   (when (slot-value sql 'components)
     (collect-table-refs (slot-value sql 'components))))
@@ -832,8 +824,100 @@ uninclusive, and the args from that keyword to the end."
 
 
 ;;
-;; Column constraint types
+;; DATABASE-OUTPUT-SQL 
+;; 
+
+(defmethod database-output-sql ((str string) database)
+  (declare (ignore database)
+           (optimize (speed 3) (safety 1) #+cmu (extensions:inhibit-warnings 3))
+           (type (simple-array * (*)) str))
+  (let ((len (length str)))
+    (declare (type fixnum len))
+    (cond ((zerop len)
+           +empty-string+)
+          ((and (null (position #\' str))
+                (null (position #\\ str)))
+           (concatenate 'string "'" str "'"))
+          (t
+           (let ((buf (make-string (+ (* len 2) 2) :initial-element #\')))
+             (do* ((i 0 (incf i))
+                   (j 1 (incf j)))
+                  ((= i len) (subseq buf 0 (1+ j)))
+               (declare (type fixnum i j))
+               (let ((char (aref str i)))
+                (declare (character char))
+                 (cond ((char= char #\')
+                        (setf (aref buf j) #\')
+                        (incf j)
+                        (setf (aref buf j) #\'))
+                       ((char= char #\\)
+                        (setf (aref buf j) #\\)
+                        (incf j)
+                        (setf (aref buf j) #\\))
+                       (t
+                        (setf (aref buf j) char))))))))))
+
+(let ((keyword-package (symbol-package :foo)))
+  (defmethod database-output-sql ((sym symbol) database)
+    (convert-to-db-default-case
+     (if (equal (symbol-package sym) keyword-package)
+        (concatenate 'string "'" (string sym) "'")
+        (symbol-name sym))
+     database)))
+
+(defmethod database-output-sql ((tee (eql t)) database)
+  (declare (ignore database))
+  "'Y'")
+
+(defmethod database-output-sql ((num number) database)
+  (declare (ignore database))
+  (princ-to-string num))
+
+(defmethod database-output-sql ((arg list) database)
+  (if (null arg) 
+      +null-string+ 
+      (format nil "(~{~A~^,~})" (mapcar #'(lambda (val)
+                                            (sql-output val database))
+                                        arg))))
+
+(defmethod database-output-sql ((arg vector) database)
+  (format nil "~{~A~^,~}" (map 'list #'(lambda (val)
+                                        (sql-output val database))
+                              arg)))
+
+(defmethod output-sql-hash-key ((arg vector) database)
+  (list 'vector (map 'list (lambda (arg)
+                             (or (output-sql-hash-key arg database)
+                                 (return-from output-sql-hash-key nil)))
+                     arg)))
+
+(defmethod database-output-sql ((self wall-time) database)
+  (declare (ignore database))
+  (db-timestring self))
+
+(defmethod database-output-sql ((self duration) database)
+  (declare (ignore database))
+  (format nil "'~a'" (duration-timestring self)))
+
+#+ignore 
+(defmethod database-output-sql ((self money) database)
+  (database-output-sql (slot-value self 'odcl::units) database))
+
+(defmethod database-output-sql (thing database)
+  (if (or (null thing)
+         (eq 'null thing))
+      +null-string+
+    (error 'sql-user-error
+           :message
+          (format nil
+                  "No type conversion to SQL for ~A is defined for DB ~A."
+                  (type-of thing) (type-of database)))))
+
+
 ;;
+;; Column constraint types and conversion to SQL 
+;;
+
 (defparameter *constraint-types*
   (list 
    (cons (symbol-name-default-case "NOT-NULL") "NOT NULL") 
@@ -847,10 +931,6 @@ uninclusive, and the args from that keyword to the end."
    (cons (symbol-name-default-case "AUTO-INCREMENT") "AUTO_INCREMENT")
    (cons (symbol-name-default-case "UNIQUE") "UNIQUE")))
 
-;;
-;; Convert type spec to sql syntax
-;;
-
 (defmethod database-constraint-statement (constraint-list database)
   (declare (ignore database))
   (make-constraints-description constraint-list))
index 470ad37d195b7f2df46feea74ca724b2ee54f3a4..943a1577ccf3538e50261e4db344d9029706cff3 100644 (file)
@@ -192,117 +192,6 @@ are nil and AV-PAIRS is an alist of (attribute value) pairs."
     (execute-command stmt :database database)))
 
 
-;; output-sql
-
-(defmethod database-output-sql ((str string) database)
-  (declare (ignore database)
-           (optimize (speed 3) (safety 1) #+cmu (extensions:inhibit-warnings 3))
-           (type (simple-array * (*)) str))
-  (let ((len (length str)))
-    (declare (type fixnum len))
-    (cond ((zerop len)
-           +empty-string+)
-          ((and (null (position #\' str))
-                (null (position #\\ str)))
-           (concatenate 'string "'" str "'"))
-          (t
-           (let ((buf (make-string (+ (* len 2) 2) :initial-element #\')))
-             (do* ((i 0 (incf i))
-                   (j 1 (incf j)))
-                  ((= i len) (subseq buf 0 (1+ j)))
-               (declare (type fixnum i j))
-               (let ((char (aref str i)))
-                (declare (character char))
-                 (cond ((char= char #\')
-                        (setf (aref buf j) #\')
-                        (incf j)
-                        (setf (aref buf j) #\'))
-                       ((char= char #\\)
-                        (setf (aref buf j) #\\)
-                        (incf j)
-                        (setf (aref buf j) #\\))
-                       (t
-                        (setf (aref buf j) char))))))))))
-
-(let ((keyword-package (symbol-package :foo)))
-  (defmethod database-output-sql ((sym symbol) database)
-    (convert-to-db-default-case
-     (if (equal (symbol-package sym) keyword-package)
-        (concatenate 'string "'" (string sym) "'")
-        (symbol-name sym))
-     database)))
-
-(defmethod database-output-sql ((tee (eql t)) database)
-  (declare (ignore database))
-  "'Y'")
-
-(defmethod database-output-sql ((num number) database)
-  (declare (ignore database))
-  (princ-to-string num))
-
-(defmethod database-output-sql ((arg list) database)
-  (if (null arg)
-      "NULL"
-      (format nil "(~{~A~^,~})" (mapcar #'(lambda (val)
-                                            (sql-output val database))
-                                        arg))))
-
-(defmethod database-output-sql ((arg vector) database)
-  (format nil "~{~A~^,~}" (map 'list #'(lambda (val)
-                                        (sql-output val database))
-                              arg)))
-
-(defmethod database-output-sql ((self wall-time) database)
-  (declare (ignore database))
-  (db-timestring self))
-
-(defmethod database-output-sql ((self duration) database)
-  (declare (ignore database))
-  (format nil "'~a'" (duration-timestring self)))
-
-(defmethod database-output-sql (thing database)
-  (if (or (null thing)
-         (eq 'null thing))
-      "NULL"
-    (error 'sql-user-error
-           :message
-          (format nil
-                  "No type conversion to SQL for ~A is defined for DB ~A."
-                  (type-of thing) (type-of database)))))
-
-
-(defmethod output-sql-hash-key ((arg vector) database)
-  (list 'vector (map 'list (lambda (arg)
-                             (or (output-sql-hash-key arg database)
-                                 (return-from output-sql-hash-key nil)))
-                     arg)))
-
-(defmethod output-sql (expr database)
-  (write-string (database-output-sql expr database) *sql-stream*)
-  (values))
-
-(defmethod output-sql ((expr list) database)
-  (if (null expr)
-      (write-string +null-string+ *sql-stream*)
-      (progn
-        (write-char #\( *sql-stream*)
-        (do ((item expr (cdr item)))
-            ((null (cdr item))
-             (output-sql (car item) database))
-          (output-sql (car item) database)
-          (write-char #\, *sql-stream*))
-        (write-char #\) *sql-stream*)))
-  t)
-
-#+nil
-(defmethod add-storage-class ((self database) (class symbol) &key (sequence t))
-  (let ((tablename (view-table (find-class class))))
-    (unless (tablep tablename)
-      (create-view-from-class class)
-      (when sequence
-        (create-sequence-from-class class)))))
-
 ;;; Iteration
 
 (defmacro do-query (((&rest args) query-expression
index 5bc74ca9e2514240a5b1f67cb5210178f4f426bc..d8066cf04a8709fbcddaf58e9c766af737ac380b 100644 (file)
@@ -125,20 +125,8 @@ value.  If nulls are allowed for the column, the slot's value will be
 nil, otherwise its value will be set to the result of calling
 DATABASE-NULL-VALUE on the type of the slot."))
 
-(defgeneric output-sql (expr database)
-  )
-
-(defgeneric output-sql-hash-key (arg database)
-  )
-
-(defgeneric collect-table-refs (sql)
-  )
-(defgeneric database-output-sql (arg database)
-  )
 (defgeneric database-pkey-constraint  (class database)
   )
-(defgeneric database-constraint-statement  (constraints database)
-  )
 (defgeneric %install-class  (class database &key transactions)
   )
 (defgeneric database-generate-column-definition  (class slotdef database)
@@ -154,3 +142,30 @@ DATABASE-NULL-VALUE on the type of the slot."))
 (defgeneric read-sql-value  (val type database db-type)
   )
 
+
+;; Generation of SQL strings from lisp expressions 
+
+(defgeneric output-sql (expr database)
+  (:documentation "Writes an SQL string appropriate for DATABASE
+  and corresponding to the lisp expression EXPR to
+  *SQL-STREAM*. The function SQL-OUTPUT is a top-level call for
+  generating SQL strings which initialises *SQL-STREAM*, calls
+  OUTPUT-SQL and reads the generated SQL string from
+  *SQL-STREAM*."))
+
+(defgeneric database-output-sql (expr database)
+  (:documentation "Returns an SQL string appropriate for DATABASE
+  and corresponding to the lisp expression
+  EXPR. DATABASE-OUTPUT-SQL is called by OUTPUT-SQL when no more
+  specific method exists for EXPR."))
+
+(defgeneric output-sql-hash-key (expr database)
+  (:documentation "Returns a list (or other object suitable for
+use as the key of an EQUAL hash table) which uniquely identifies
+the arguments EXPR and DATABASE."))
+
+(defgeneric collect-table-refs (sql)
+  )
+
+(defgeneric database-constraint-statement  (constraints database)
+  )
index d44f0221b420e9bda065995cf718c114e483e3de..3ec173a40576e7534e8145d67791322ef7824660 100644 (file)
@@ -108,8 +108,8 @@ in DATABASE which defaults to *DEFAULT-DATABASE*."
     (when keylist 
       (convert-to-db-default-case
        (format nil "CONSTRAINT ~APK PRIMARY KEY~A"
-              (database-output-sql (view-table class) database)
-              (database-output-sql keylist database))
+              (sql-output (view-table class) database)
+              (sql-output keylist database))
        database))))
 
 (defmethod database-generate-column-definition (class slotdef database)