r9364: Various fixes from CommonSQL Tutorial.
authorMarcus Pearce <m.t.pearce@city.ac.uk>
Sun, 16 May 2004 01:05:48 +0000 (01:05 +0000)
committerMarcus Pearce <m.t.pearce@city.ac.uk>
Sun, 16 May 2004 01:05:48 +0000 (01:05 +0000)
ChangeLog
TODO
db-postgresql-socket/postgresql-socket-sql.lisp
db-postgresql/postgresql-sql.lisp
sql/classes.lisp
sql/operations.lisp
sql/sql.lisp
tests/test-basic.lisp
tests/test-fdml.lisp
tests/test-syntax.lisp

index ada36bdd6d6c3a1dc58267c309bfb65a51e5c11e..c555bdcd93da39f673e3612d45d06e62983f0add 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,11 +1,35 @@
 15 May 2004 Marcus Pearce (m.t.pearce@city.ac.uk) 
-       * sql/classes.lisp: SELECT now accepts table identifiers as strings 
-       for CommonSQL compliance. Add support for qualified sql identifiers 
-       with aliased table names. 
-       * tests/test-fdml.lisp: added tests for table identifiers as strings 
-       in SELECT and for aliased definitions. 
-       * tests/test-syntax.lisp: added tests for alias definitions. 
-
+       * sql/operations.lisp: make MINUS operator a synonym for EXCEPT. Add 
+       COALESCE operator and make NVL a synonym for this. Make ANY, SOME, 
+       ALL and EXISTS generate function expressions so they output the 
+       correct SQL. 
+       * sql/classes.lisp: SELECT now generates appropriate SQL when 
+       passed the SET-OPERATION and ALL keyword arguments. 
+       * sql/classes.lisp: the ORDER-BY keyword argument to SELECT now 
+       accepts ordering constraints as pairs of the form (column direction) 
+       where direction may be :ASC or :DESC. 
+       * tests/test-syntax.lisp: added tests for MINUS and COALESCE/NVL. 
+       Correct tests for ANY, SOME, ALL and EXISTS. 
+       * tests/test-fdml.lisp: added test for COALESCE. 
+       * sql/sql.lisp: MAP-QUERY now applies FUNCTION to QUERY-EXPRESSION 
+       using funcall unless QUERY-EXPRESSION returns one column and its 
+       FLATP slot is not nil in which case apply is used. 
+       * tests/test-basic.lisp: modified calls to MAP-QUERY to reflect the 
+       changes. 
+       * TODO: remove items done. 
+       * db-postgresql/postgresql-sql.lisp: no need to reverse results in 
+       DATABASE-LIST-ATTRIBUTES. 
+       * db-postgresql-socket/postgresql-socket-sql.lisp: no need to reverse
+       results in DATABASE-LIST-ATTRIBUTES.    
+       
+15 May 2004 Marcus Pearce (m.t.pearce@city.ac.uk) 
+        * sql/classes.lisp: SELECT now accepts table identifiers as strings 
+        for CommonSQL compliance. Add support for qualified sql identifiers 
+        with aliased table names. 
+        * tests/test-fdml.lisp: added tests for table identifiers as strings 
+        in SELECT and for aliased definitions. 
+        * tests/test-syntax.lisp: added tests for alias definitions. 
+       
 15 May 2004 Marcus Pearce (m.t.pearce@city.ac.uk) 
        * sql/sql.lisp: PRINT-QUERY now calls QUERY with result-types and 
        field-names set to nil. 
diff --git a/TODO b/TODO
index 8f59a616bc99d3ccd16fd63dd861cad6c5bbb15c..bf7884a19001bb60db9692a1ac6d671d48db124e 100644 (file)
--- a/TODO
+++ b/TODO
@@ -22,14 +22,7 @@ COMMONSQL SPEC
 
  >> Symbolic SQL syntax 
 
-      o Complete sql expressions (see operations.lisp)
-
-         nvl (Oracle specific) 
-         userenv (Oracle specific) 
-        minus (Oracle specific: does the same as EXCEPT) 
-
-      o variables (e.g., table identifiers) should be instantiated at runtime 
-
+      o userenv (Oracle specific but deprecated in Oracle 9) 
 
 VARIANCES FROM COMMONSQL
 
index ebda22c0a7b4557c82504aeedb825b80ad314702..a0a534aa0392ba8f65fda49fdc261c6a3f31b290 100644 (file)
@@ -392,16 +392,15 @@ doesn't depend on UFFI."
                            owner-clause)
                    database nil nil))))
     (if result
-       (reverse
-         (remove-if #'(lambda (it) (member it '("cmin"
-                                                "cmax"
-                                                "xmax"
-                                                "xmin"
-                                               "oid"
-                                                "ctid"
-                                               ;; kmr -- added tableoid
-                                               "tableoid") :test #'equal)) 
-                   result)))))
+        (remove-if #'(lambda (it) (member it '("cmin"
+                                               "cmax"
+                                               "xmax"
+                                               "xmin"
+                                               "oid"
+                                               "ctid"
+                                               ;; kmr -- added tableoid
+                                               "tableoid") :test #'equal)) 
+                   result))))
 
 (defmethod database-attribute-type (attribute (table string)
                                    (database postgresql-socket-database)
index 2bb7fb12efcb9306afa1bbc7de037d276f8cddaf..0dc3f57d2a3e1585f89820d1fd4eb5cfa8a9c542 100644 (file)
                            owner-clause)
                    database nil nil))))
     (if result
-       (reverse
-         (remove-if #'(lambda (it) (member it '("cmin"
-                                                "cmax"
-                                                "xmax"
-                                                "xmin"
-                                               "oid"
-                                                "ctid"
-                                               ;; kmr -- added tableoid
-                                               "tableoid") :test #'equal)) 
-                   result)))))
+        (remove-if #'(lambda (it) (member it '("cmin"
+                                               "cmax"
+                                               "xmax"
+                                               "xmin"
+                                               "oid"
+                                               "ctid"
+                                               ;; kmr -- added tableoid
+                                               "tableoid") :test #'equal)) 
+                   result))))
 
 (defmethod database-attribute-type (attribute (table string)
                                    (database postgresql-database)
index 872830f7ebf1bba5e39e66fb5e568a73c77eb35a..af3ca8db3fdbecd86ad9719d1a886387a3372f92 100644 (file)
     (let ((subs (if (consp (car sub-expressions))
                     (car sub-expressions)
                     sub-expressions)))
+      (when (= (length subs) 1)
+        (output-sql operator database)
+        (write-char #\Space *sql-stream*))
       (do ((sub subs (cdr sub)))
           ((null (cdr sub)) (output-sql (car sub) database))
         (output-sql (car sub) database)
@@ -568,12 +571,15 @@ uninclusive, and the args from that keyword to the end."
 
 (defmethod output-sql ((query sql-query) database)
   (with-slots (distinct selections from where group-by having order-by
-                        order-by-descending limit offset inner-join on)
+                        order-by-descending limit offset inner-join on
+                        all set-operation) 
       query
     (when *in-subselect*
       (write-string "(" *sql-stream*))
     (write-string "SELECT " *sql-stream*)
-    (when distinct
+    (when all 
+      (write-string "ALL " *sql-stream*))
+    (when (and distinct (not all))
       (write-string "DISTINCT " *sql-stream*)
       (unless (eql t distinct)
         (write-string "ON " *sql-stream*)
@@ -609,9 +615,15 @@ uninclusive, and the args from that keyword to the end."
       (if (listp order-by)
           (do ((order order-by (cdr order)))
               ((null order))
-            (output-sql (car order) database)
-            (when (cdr order)
-              (write-char #\, *sql-stream*)))
+            (let ((item (car order)))
+              (typecase item 
+                (cons 
+                 (output-sql (car item) database)
+                 (format *sql-stream* " ~A" (cadr item)))
+                (t 
+                 (output-sql item database)))
+              (when (cdr order)
+                (write-char #\, *sql-stream*))))
           (output-sql order-by database)))
     (when order-by-descending
       (write-string " ORDER BY " *sql-stream*)
@@ -630,7 +642,10 @@ uninclusive, and the args from that keyword to the end."
       (write-string " OFFSET " *sql-stream*)
       (output-sql offset database))
     (when *in-subselect*
-      (write-string ")" *sql-stream*)))
+      (write-string ")" *sql-stream*))
+    (when set-operation 
+      (write-char #\Space *sql-stream*)
+      (output-sql set-operation database)))
   t)
 
 (defmethod output-sql ((query sql-object-query) database)
index bdbb9294ca33500e8583c2904cc84d1aea8c3bcf..bc99d2a136eaf02297f8747a9c8e9eb250178393 100644 (file)
   (apply #'make-query args))
 
 (defsql sql-any (:symbol "any") (&rest rest)
-  (make-instance 'sql-value-exp
-                :modifier 'any :components rest))
+  (make-instance 'sql-function-exp
+                :name 'any :args rest))
 
 (defsql sql-some (:symbol "some") (&rest rest)
-  (make-instance 'sql-value-exp
-                :modifier 'some :components rest))
+  (make-instance 'sql-function-exp
+                :name 'some :args rest))
 
 (defsql sql-all (:symbol "all") (&rest rest)
-  (make-instance 'sql-value-exp
-                :modifier 'all :components rest))
+  (make-instance 'sql-function-exp
+                :name 'all :args rest))
 
 (defsql sql-not (:symbol "not") (&rest rest)
   (make-instance 'sql-value-exp
@@ -65,8 +65,8 @@
                 :operator 'except :sub-expressions rest))
 
 (defsql sql-minus (:symbol "minus") (&rest rest)
-  (make-instance 'sql-value-exp
-                :modifier 'minus :components rest))
+  (make-instance 'sql-set-exp 
+                :operator 'except :sub-expressions rest))
 
 (defsql sql-limit (:symbol "limit") (&rest rest)
   (make-instance 'sql-query-modifier-exp 
@@ -95,8 +95,8 @@
                 :components '|NOT NULL|))
 
 (defsql sql-exists (:symbol "exists") (&rest rest)
-  (make-instance 'sql-value-exp
-                :modifier 'exists :components rest))
+  (make-instance 'sql-function-exp
+                :name 'exists :args rest))
 
 (defsql sql-* (:symbol "*") (&rest rest)
   (if (zerop (length rest))
 
 (defsql sql-in (:symbol "in") (&rest rest)
   (make-instance 'sql-relational-exp
-                :operator 'in :sub-expressions rest))
+                 :operator 'in :sub-expressions rest))
 
 (defsql sql-concat (:symbol "concat") (&rest rest)
   (make-instance 'sql-relational-exp
   (make-instance 'sql-query-modifier-exp :modifier 'distinct 
                 :components rest))
 
+(defsql sql-coalesce (:symbol "coalesce") (&rest rest)
+  (make-instance 'sql-function-exp
+                :name 'coalesce :args rest))
+
+(defsql sql-nvl (:symbol "nvl") (&rest rest)
+  (make-instance 'sql-function-exp
+                :name 'coalesce :args rest))
index 0a733aa81035c787e3a12d2c51bf0eb010b4f5a2..6bc454724621bf4adbbc76ddfdbae63caff5b9a3 100644 (file)
@@ -361,62 +361,85 @@ MAP."
   (multiple-value-bind (result-set columns)
       (database-query-result-set query-expression database :full-set nil
                                 :result-types result-types)
-    (when result-set
-      (unwind-protect
-          (do ((row (make-list columns)))
-              ((not (database-store-next-row result-set database row))
-               nil)
-            (apply function row))
-       (database-dump-result-set result-set database)))))
+    (let ((flatp (and (= columns 1) 
+                      (typecase query-expression 
+                        (string t) 
+                        (sql-query 
+                         (slot-value query-expression 'flatp))))))
+      (when result-set
+        (unwind-protect
+             (do ((row (make-list columns)))
+                 ((not (database-store-next-row result-set database row))
+                  nil)
+               (if flatp
+                   (apply function row)
+                   (funcall function row)))
+          (database-dump-result-set result-set database))))))
                     
 (defun map-query-to-list (function query-expression database result-types)
   (multiple-value-bind (result-set columns)
       (database-query-result-set query-expression database :full-set nil
                                 :result-types result-types)
-    (when result-set
-      (unwind-protect
-          (let ((result (list nil)))
-            (do ((row (make-list columns))
-                 (current-cons result (cdr current-cons)))
-                ((not (database-store-next-row result-set database row))
-                 (cdr result))
-              (rplacd current-cons (list (apply function row)))))
-       (database-dump-result-set result-set database)))))
-
+    (let ((flatp (and (= columns 1) 
+                      (typecase query-expression 
+                        (string t) 
+                        (sql-query 
+                         (slot-value query-expression 'flatp))))))
+      (when result-set
+        (unwind-protect
+             (let ((result (list nil)))
+               (do ((row (make-list columns))
+                    (current-cons result (cdr current-cons)))
+                   ((not (database-store-next-row result-set database row))
+                    (cdr result))
+                 (rplacd current-cons 
+                         (list (if flatp 
+                                   (apply function row)
+                                   (funcall function (copy-list row)))))))
+          (database-dump-result-set result-set database))))))
 
 (defun map-query-to-simple (output-type-spec function query-expression database result-types)
   (multiple-value-bind (result-set columns rows)
       (database-query-result-set query-expression database :full-set t
                                 :result-types result-types)
-    (when result-set
-      (unwind-protect
-          (if rows
-              ;; We know the row count in advance, so we allocate once
-              (do ((result
-                    (cmucl-compat:make-sequence-of-type output-type-spec rows))
-                   (row (make-list columns))
-                   (index 0 (1+ index)))
-                  ((not (database-store-next-row result-set database row))
-                   result)
-                (declare (fixnum index))
-                (setf (aref result index)
-                      (apply function row)))
-              ;; Database can't report row count in advance, so we have
-              ;; to grow and shrink our vector dynamically
-              (do ((result
-                    (cmucl-compat:make-sequence-of-type output-type-spec 100))
-                   (allocated-length 100)
-                   (row (make-list columns))
-                   (index 0 (1+ index)))
-                  ((not (database-store-next-row result-set database row))
-                   (cmucl-compat:shrink-vector result index))
-                (declare (fixnum allocated-length index))
-                (when (>= index allocated-length)
-                  (setq allocated-length (* allocated-length 2)
-                        result (adjust-array result allocated-length)))
-                (setf (aref result index)
-                      (apply function row))))
-       (database-dump-result-set result-set database)))))
+    (let ((flatp (and (= columns 1) 
+                      (typecase query-expression 
+                        (string t) 
+                        (sql-query
+                         (slot-value query-expression 'flatp))))))
+      (when result-set
+        (unwind-protect
+             (if rows
+                 ;; We know the row count in advance, so we allocate once
+                 (do ((result
+                       (cmucl-compat:make-sequence-of-type output-type-spec rows))
+                      (row (make-list columns))
+                      (index 0 (1+ index)))
+                     ((not (database-store-next-row result-set database row))
+                      result)
+                   (declare (fixnum index))
+                   (setf (aref result index)
+                         (if flatp 
+                             (apply function row)
+                             (funcall function (copy-list row)))))
+                 ;; Database can't report row count in advance, so we have
+                 ;; to grow and shrink our vector dynamically
+                 (do ((result
+                       (cmucl-compat:make-sequence-of-type output-type-spec 100))
+                      (allocated-length 100)
+                      (row (make-list columns))
+                      (index 0 (1+ index)))
+                     ((not (database-store-next-row result-set database row))
+                      (cmucl-compat:shrink-vector result index))
+                   (declare (fixnum allocated-length index))
+                   (when (>= index allocated-length)
+                     (setq allocated-length (* allocated-length 2)
+                           result (adjust-array result allocated-length)))
+                   (setf (aref result index)
+                         (if flatp 
+                             (apply function row)
+                             (funcall function (copy-list row))))))
+          (database-dump-result-set result-set database))))))
 
 ;;; Row processing macro from CLSQL
 
index 6a27fdd7de0d5f1f7883bfbf2d2044235dd184d0..598879bd1aca96e01aa2e1eb0b498de7979d0fe1 100644 (file)
 
     (deftest :BASIC/MAP/1
        (let ((results '())
-             (rows (map-query 'vector #'list "select * from TYPE_TABLE" 
+             (rows (map-query 'vector #'identity "select * from TYPE_TABLE" 
                               :result-types nil)))
          (declare (type (simple-array list (*)) rows))
          (dotimes (i (length rows) results)
     
     (deftest :BASIC/MAP/2
        (let ((results '())
-             (rows (map-query 'list #'list "select * from TYPE_TABLE" 
+             (rows (map-query 'list #'identity "select * from TYPE_TABLE" 
                               :result-types nil)))
          (dotimes (i (length rows) results)
            (push
     
     (deftest :BASIC/MAP/3
            (let ((results '())
-             (rows (map-query 'list #'list "select * from TYPE_TABLE" 
+             (rows (map-query 'list #'identity "select * from TYPE_TABLE" 
                               :result-types :auto)))
              (dotimes (i (length rows) results)
                (push
index 33267a52f33d580866d7017f513fa09b7d8cc942..f764e73ae28db0c446fde6e42f9afdf57f2349c6 100644 (file)
   :order-by '(["table" last-name])
   :result-types nil :field-names nil)
  (("Vladamir" "Lenin") ("Vladamir" "Putin")))
+
+(deftest :fdml/select/27 
+ (clsql:select [coalesce [managerid] 10] :from [employee] :order-by [emplid]
+  :field-names nil :result-types nil :flatp t)
+ ("10" "1" "1" "1" "1" "1" "1" "1" "1" "1"))
   
 ;(deftest :fdml/select/11
 ;    (clsql:select [emplid] :from [employee]
index 8d1a86382a668680e53dba26247753cb3941dbe9..8591817cf58cf58d68c53d50942fc3b26f8ed34c 100644 (file)
@@ -73,7 +73,7 @@
 
 (deftest :syntax/subquery/1
     (clsql:sql [any '(3 4)])
 "(ANY ((3,4)))")
"ANY((3,4))")
 
 (deftest :syntax/subquery/2
     (clsql:sql [in [foo] '(foo bar baz)])
 
 (deftest :syntax/subquery/3
     (clsql:sql [all '(foo bar baz)])
-  "(ALL ((FOO,BAR,BAZ)))")
+  "ALL((FOO,BAR,BAZ))")
 
 (deftest :syntax/subquery/4
     (clsql:sql [exists '(foo bar baz)])
-  "(EXISTS ((FOO,BAR,BAZ)))")
+  "EXISTS((FOO,BAR,BAZ))")
 
 (deftest :syntax/subquery/5
     (clsql:sql [some '(foo bar baz)])
-  "(SOME ((FOO,BAR,BAZ)))")
+  "SOME((FOO,BAR,BAZ))")
 
 
 (deftest :syntax/aggregate/1 
  "GROUP BY FOO")
 
 
+(deftest :syntax/coalesce/1 
+    (clsql:sql [coalesce [foo] [bar] "not specified"])
+ "COALESCE(FOO,BAR,'not specified')")
+
+(deftest :syntax/coalesce/2
+    (clsql:sql [nvl [foo] "not specified"])
+ "COALESCE(FOO,'not specified')")
+
+
 (deftest :syntax/sets/1 
     (clsql:sql [union [select [foo] :from [bar]] [select [baz] :from [bar]]])
  "SELECT FOO FROM BAR UNION SELECT BAZ FROM BAR")
     (clsql:sql [except [select [foo] :from [bar]] [select [baz] :from [bar]]])
  "SELECT FOO FROM BAR EXCEPT SELECT BAZ FROM BAR")
 
+(deftest :syntax/sets/4
+    (clsql:sql [minus [select [foo] :from [bar]] [select [baz] :from [bar]]])
+ "SELECT FOO FROM BAR EXCEPT SELECT BAZ FROM BAR")
+
 
 (deftest :syntax/function/1
     (clsql:sql [function "COS" [age]])