X-Git-Url: http://git.kpe.io/?a=blobdiff_plain;f=sql%2Fsql.lisp;h=0397bd031ca01984702a667bd031bba537fd824c;hb=1619f599a1e37dd30dfe7ab803374f5eed26544a;hp=28d5a922b857a4f9dd9387f497ddcc02d5a7c2f1;hpb=79dacf4fb7e6707e815c3e8072e5c809acaa9386;p=clsql.git diff --git a/sql/sql.lisp b/sql/sql.lisp index 28d5a92..0397bd0 100644 --- a/sql/sql.lisp +++ b/sql/sql.lisp @@ -12,11 +12,10 @@ ;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL. ;;;; ************************************************************************* -(in-package #:clsql-sys) +(in-package #:clsql) ;;; Basic operations on databases - (defmethod database-query-result-set ((expr %sql-expression) database &key full-set result-types) (database-query-result-set (sql-output expr database) database @@ -29,20 +28,34 @@ (defmethod query ((expr %sql-expression) &key (database *default-database*) - (result-types nil) (flatp nil)) + (result-types :auto) (flatp nil) (field-names t)) (query (sql-output expr database) :database database :flatp flatp - :result-types result-types)) + :result-types result-types :field-names field-names)) + +(defmethod query ((expr sql-object-query) &key (database *default-database*) + (result-types :auto) (flatp nil)) + (declare (ignore result-types)) + (apply #'select (append (slot-value expr 'objects) + (slot-value expr 'exp) + (when (slot-value expr 'refresh) + (list :refresh (sql-output expr database))) + (when (or flatp (slot-value expr 'flatp) ) + (list :flatp t)) + (list :database database)))) -(defun truncate-database (database) +(defun truncate-database (&key (database *default-database*)) (unless (typep database 'database) - (clsql-base-sys::signal-no-database-error database)) + (clsql-base::signal-no-database-error database)) (unless (is-database-open database) (database-reconnect database)) - (dolist (table (list-tables database)) + (when (db-type-has-views? (database-underlying-type database)) + (dolist (view (list-views :database database)) + (drop-view view :database database))) + (dolist (table (list-tables :database database)) (drop-table table :database database)) - (dolist (index (list-indexes database)) + (dolist (index (list-indexes :database database)) (drop-index index :database database)) - (dolist (seq (list-sequences database)) + (dolist (seq (list-sequences :database database)) (drop-sequence seq :database database))) (defun print-query (query-exp &key titles (formats t) (sizes t) (stream t) @@ -66,7 +79,7 @@ value of T. This specifies that *STANDARD-OUTPUT* is used." (mapcan #'(lambda (s f) (list s f)) sizes record))))) (let* ((query-exp (etypecase query-exp (string query-exp) - (sql-query (sql-output query-exp)))) + (sql-query (sql-output query-exp database)))) (data (query query-exp :database database)) (sizes (if (or (null sizes) (listp sizes)) sizes (compute-sizes (if titles (cons titles data) data)))) @@ -188,10 +201,11 @@ condition is true." (let ((keyword-package (symbol-package :foo))) (defmethod database-output-sql ((sym symbol) database) - (declare (ignore database)) - (if (equal (symbol-package sym) keyword-package) - (concatenate 'string "'" (string sym) "'") - (symbol-name sym)))) + (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)) @@ -217,6 +231,10 @@ condition is true." (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)) @@ -227,17 +245,17 @@ condition is true." :format-arguments (list (type-of thing) (type-of database))))) -(defmethod output-sql-hash-key ((arg vector) &optional 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 &optional (database *default-database*)) +(defmethod output-sql (expr database) (write-string (database-output-sql expr database) *sql-stream*) (values)) -(defmethod output-sql ((expr list) &optional (database *default-database*)) +(defmethod output-sql ((expr list) database) (if (null expr) (write-string +null-string+ *sql-stream*) (progn @@ -254,7 +272,8 @@ condition is true." &key (database *default-database*)) (database-describe-table database - (string-downcase (symbol-name (slot-value table 'name))))) + (convert-to-db-default-case + (symbol-name (slot-value table 'name)) database))) #+nil (defmethod add-storage-class ((self database) (class symbol) &key (sequence t)) @@ -264,3 +283,133 @@ condition is true." (when sequence (create-sequence-from-class class))))) +;;; Iteration + + +(defmacro do-query (((&rest args) query-expression + &key (database '*default-database*) (result-types :auto)) + &body body) + "Repeatedly executes BODY within a binding of ARGS on the +attributes of each record resulting from QUERY-EXPRESSION. The +return value is determined by the result of executing BODY. The +default value of DATABASE is *DEFAULT-DATABASE*." + (let ((result-set (gensym "RESULT-SET-")) + (qe (gensym "QUERY-EXPRESSION-")) + (columns (gensym "COLUMNS-")) + (row (gensym "ROW-")) + (db (gensym "DB-"))) + `(let ((,qe ,query-expression)) + (typecase ,qe + (sql-object-query + (dolist (,row (query ,qe)) + (destructuring-bind ,args + ,row + ,@body))) + (t + ;; Functional query + (let ((,db ,database)) + (multiple-value-bind (,result-set ,columns) + (database-query-result-set ,qe ,db + :full-set nil + :result-types ,result-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*) + (result-types :auto)) + "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." + (typecase query-expression + (sql-object-query + (map output-type-spec #'(lambda (x) (apply function x)) + (query query-expression))) + (t + ;; Functional query + (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 + result-types)) + (list + (map-query-to-list function query-expression database result-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 result-types)) + (t + (funcall #'map-query + (cmucl-compat:result-type-or-lose output-type-spec t) + function query-expression :database database + :result-types result-types))))))) + +(defun map-query-for-effect (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 + (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 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))))) + + +(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)))))