r8832: changes for allow import of clsql and clsql-usql in the same package
[clsql.git] / base / basic-sql.lisp
index df5d93b840c27ae9218718668b9bc5e0b5294806..ea9245e5aa8e03b8a070e1b0ff68a20eaa096592 100644 (file)
@@ -50,3 +50,111 @@ pair."))
   (values))
 
 
+(defmacro do-query (((&rest args) query-expression
+                    &key (database '*default-database*) (types nil))
+                   &body body)
+  "Repeatedly executes BODY within a binding of ARGS on the attributes
+of each record resulting from QUERY. The return value is determined by
+the result of executing BODY. The default value of DATABASE is
+*DEFAULT-DATABASE*."
+  (let ((result-set (gensym))
+       (columns (gensym))
+       (row (gensym))
+       (db (gensym)))
+    `(let ((,db ,database))
+      (multiple-value-bind (,result-set ,columns)
+          (database-query-result-set ,query-expression ,db
+                                     :full-set nil :types ,types)
+        (when ,result-set
+          (unwind-protect
+               (do ((,row (make-list ,columns)))
+                   ((not (database-store-next-row ,result-set ,db ,row))
+                    nil)
+                 (destructuring-bind ,args ,row
+                   ,@body))
+            (database-dump-result-set ,result-set ,db)))))))
+
+(defun map-query (output-type-spec function query-expression
+                 &key (database *default-database*)
+                 (types nil))
+  "Map the function over all tuples that are returned by the query in
+query-expression.  The results of the function are collected as
+specified in output-type-spec and returned like in MAP."
+  (macrolet ((type-specifier-atom (type)
+              `(if (atom ,type) ,type (car ,type))))
+    (case (type-specifier-atom output-type-spec)
+      ((nil) 
+       (map-query-for-effect function query-expression database types))
+      (list 
+       (map-query-to-list function query-expression database types))
+      ((simple-vector simple-string vector string array simple-array
+       bit-vector simple-bit-vector base-string
+       simple-base-string)
+       (map-query-to-simple output-type-spec function query-expression database types))
+      (t
+       (funcall #'map-query (cmucl-compat:result-type-or-lose output-type-spec t)
+              function query-expression :database database :types types)))))
+
+(defun map-query-for-effect (function query-expression database types)
+  (multiple-value-bind (result-set columns)
+      (database-query-result-set query-expression database :full-set nil
+                                :types 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)))))
+                    
+(defun map-query-to-list (function query-expression database types)
+  (multiple-value-bind (result-set columns)
+      (database-query-result-set query-expression database :full-set nil
+                                :types 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)))))
+
+
+(defun map-query-to-simple (output-type-spec function query-expression database types)
+  (multiple-value-bind (result-set columns rows)
+      (database-query-result-set query-expression database :full-set t
+                                :types 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)))))
+
+
+