X-Git-Url: http://git.kpe.io/?a=blobdiff_plain;f=sql%2Fobjects.lisp;h=1c309751883cefc56a3f168088d01b75c9d1c5c4;hb=8c6c643e3debe875bd14408cc3129d8148dfd125;hp=04951f9c0fe07419f7bb8d8d754d9dc3c9cb47d0;hpb=1619f599a1e37dd30dfe7ab803374f5eed26544a;p=clsql.git diff --git a/sql/objects.lisp b/sql/objects.lisp index 04951f9..1c30975 100644 --- a/sql/objects.lisp +++ b/sql/objects.lisp @@ -13,7 +13,7 @@ ;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL. ;;;; ************************************************************************* -(in-package #:clsql) +(in-package #:clsql-sys) (defclass standard-db-object () ((view-database :initform nil :initarg :view-database :reader view-database @@ -182,7 +182,7 @@ superclass of the newly-defined View Class." (defclass ,class ,supers ,slots ,@(if (find :metaclass `,cl-options :key #'car) `,cl-options - (cons '(:metaclass clsql::standard-db-class) `,cl-options))) + (cons '(:metaclass clsql-sys::standard-db-class) `,cl-options))) (finalize-inheritance (find-class ',class)) (find-class ',class))) @@ -317,10 +317,10 @@ superclass of the newly-defined View Class." (basetype (if (listp slot-type) (car slot-type) slot-type))) (when (and slot-type val) (unless (typep val basetype) - (error 'clsql-type-error - :slotname (slot-definition-name slotdef) - :typespec slot-type - :value val))))) + (error 'sql-user-error + :message + (format nil "Invalid value ~A in slot ~A, not of type ~A." + val (slot-definition-name slotdef) slot-type)))))) ;; ;; Called by find-all @@ -423,7 +423,7 @@ superclass of the newly-defined View Class." (let ((qualifier (key-qualifier-for-instance instance :database vd))) (delete-records :from vt :where qualifier :database vd) (setf (slot-value instance 'view-database) nil)) - (error 'clsql-base::clsql-no-database-error :database nil)))) + (signal-no-database-error vd)))) (defmethod update-instance-from-records ((instance standard-db-object) &key (database *default-database*)) @@ -482,7 +482,7 @@ superclass of the newly-defined View Class." (defmethod database-get-type-specifier (type args database) (declare (ignore type args)) - (if (clsql-base::in (database-underlying-type database) + (if (in (database-underlying-type database) :postgresql :postgresql-socket) "VARCHAR" "VARCHAR(255)")) @@ -506,7 +506,7 @@ superclass of the newly-defined View Class." database) (if args (format nil "VARCHAR(~A)" (car args)) - (if (clsql-base::in (database-underlying-type database) + (if (in (database-underlying-type database) :postgresql :postgresql-socket) "VARCHAR" "VARCHAR(255)"))) @@ -515,7 +515,7 @@ superclass of the newly-defined View Class." database) (if args (format nil "VARCHAR(~A)" (car args)) - (if (clsql-base::in (database-underlying-type database) + (if (in (database-underlying-type database) :postgresql :postgresql-socket) "VARCHAR" "VARCHAR(255)"))) @@ -523,7 +523,7 @@ superclass of the newly-defined View Class." (defmethod database-get-type-specifier ((type (eql 'string)) args database) (if args (format nil "VARCHAR(~A)" (car args)) - (if (clsql-base::in (database-underlying-type database) + (if (in (database-underlying-type database) :postgresql :postgresql-socket) "VARCHAR" "VARCHAR(255)"))) @@ -587,7 +587,7 @@ superclass of the newly-defined View Class." (declare (ignore database)) (progv '(*print-circle* *print-array*) '(t t) (let ((escaped (prin1-to-string val))) - (clsql-base::substitute-char-string + (substitute-char-string escaped #\Null " ")))) (defmethod database-output-sql-as-type ((type (eql 'symbol)) val database) @@ -667,8 +667,8 @@ superclass of the newly-defined View Class." (defmethod read-sql-value (val (type (eql 'symbol)) database) (declare (ignore database)) (when (< 0 (length val)) - (unless (string= val (clsql-base:symbol-name-default-case "NIL")) - (intern (clsql-base:symbol-name-default-case val) + (unless (string= val (symbol-name-default-case "NIL")) + (intern (symbol-name-default-case val) (symbol-package *update-context*))))) (defmethod read-sql-value (val (type (eql 'integer)) database) @@ -806,16 +806,61 @@ superclass of the newly-defined View Class." (defun update-object-joins (objects &key (slots t) (force-p t) class-name (max-len *default-update-objects-max-len*)) - "Updates the remote join slots, that is those slots defined without :retrieval :immediate." + "Updates the remote join slots, that is those slots defined without +:retrieval :immediate." + (assert (or (null max-len) (plusp max-len))) (when objects (unless class-name - (class-name (class-of (first objects)))) + (setq class-name (class-name (class-of (first objects))))) (let* ((class (find-class class-name)) - (deferred-joins (generate-retrieval-joins-list class :deferred))) - (when deferred-joins - (warn "not yet implemented.") - )))) - + (class-slots (ordered-class-slots class)) + (slotdefs + (if (eq t slots) + (generate-retrieval-joins-list class :deferred) + (remove-if #'null + (mapcar #'(lambda (name) + (let ((slotdef (find name class-slots :key #'slot-definition-name))) + (unless slotdef + (warn "Unable to find slot named ~S in class ~S." name class)) + slotdef)) + slots))))) + (dolist (slotdef slotdefs) + (let* ((dbi (view-class-slot-db-info slotdef)) + (slotdef-name (slot-definition-name slotdef)) + (foreign-key (gethash :foreign-key dbi)) + (home-key (gethash :home-key dbi)) + (object-keys + (remove-duplicates + (if force-p + (mapcar #'(lambda (o) (slot-value o home-key)) objects) + (remove-if #'null + (mapcar + #'(lambda (o) (if (slot-boundp o slotdef-name) + nil + (slot-value o home-key))) + objects))))) + (n-object-keys (length object-keys)) + (query-len (or max-len n-object-keys))) + + (do ((i 0 (+ i query-len))) + ((>= i n-object-keys)) + (let* ((keys (if max-len + (subseq object-keys i (min (+ i query-len) n-object-keys)) + object-keys)) + (results (find-all (list (gethash :join-class dbi)) + :where (make-instance 'sql-relational-exp + :operator 'in + :sub-expressions (list (sql-expression :attribute foreign-key) + keys)) + :flatp t))) + (dolist (object objects) + (when (or force-p (not (slot-boundp object slotdef-name))) + (let ((res (find (slot-value object home-key) results + :key #'(lambda (res) (slot-value res foreign-key)) + :test #'equal))) + (when res + (setf (slot-value object slotdef-name) res))))))))))) + (values)) (defun fault-join-slot-raw (class object slot-def) (let* ((dbi (view-class-slot-db-info slot-def)) @@ -882,49 +927,51 @@ superclass of the newly-defined View Class." ;; For example, for (select 'employee-address) in test suite => ;; select addr.*,ea_join.* FROM addr,ea_join WHERE ea_join.aaddressid=addr.addressid\g -(defun build-objects (vals sclasses immediate-join-classes sels immediate-joins database refresh flatp) +(defun build-objects (vals sclasses immediate-join-classes sels immediate-joins database refresh flatp instances) "Used by find-all to build objects." - (labels ((build-object (vals vclass jclasses selects immediate-selects) - (let* ((class-name (class-name vclass)) - (db-vals (butlast vals (- (list-length vals) + (labels ((build-object (vals vclass jclasses selects immediate-selects instance) + (let* ((db-vals (butlast vals (- (list-length vals) (list-length selects)))) + (obj (if instance instance (make-instance (class-name vclass) :view-database database))) (join-vals (subseq vals (list-length selects))) - (obj (make-instance class-name :view-database database)) (joins (mapcar #'(lambda (c) (when c (make-instance c :view-database database))) jclasses))) ;;(format t "db-vals: ~S, join-values: ~S~%" db-vals join-vals) ;; use refresh keyword here - (setf obj (get-slot-values-from-view obj (mapcar #'car selects) - db-vals)) + (setf obj (get-slot-values-from-view obj (mapcar #'car selects) db-vals)) (mapc #'(lambda (jc) (get-slot-values-from-view jc (mapcar #'car immediate-selects) join-vals)) joins) (mapc - #'(lambda (jc) (let ((slot (find (class-name (class-of jc)) (class-slots vclass) - :key #'(lambda (slot) (when (and (eq :join (view-class-slot-db-kind slot)) - (eq (slot-definition-name slot) - (gethash :join-class (view-class-slot-db-info slot)))) - (slot-definition-name slot)))))) - (when slot - (setf (slot-value obj (slot-definition-name slot)) jc)))) - + #'(lambda (jc) + (let ((slot (find (class-name (class-of jc)) (class-slots vclass) + :key #'(lambda (slot) + (when (and (eq :join (view-class-slot-db-kind slot)) + (eq (slot-definition-name slot) + (gethash :join-class (view-class-slot-db-info slot)))) + (slot-definition-name slot)))))) + (when slot + (setf (slot-value obj (slot-definition-name slot)) jc)))) joins) (when refresh (instance-refreshed obj)) obj))) - (let ((objects (mapcar #'(lambda (sclass jclass sel immediate-join) - (prog1 (build-object vals sclass jclass sel immediate-join) - (setf vals (nthcdr (+ (list-length sel) (list-length immediate-join)) - vals)))) - sclasses immediate-join-classes sels immediate-joins))) + (let* ((objects + (mapcar #'(lambda (sclass jclass sel immediate-join instance) + (prog1 + (build-object vals sclass jclass sel immediate-join instance) + (setf vals (nthcdr (+ (list-length sel) (list-length immediate-join)) + vals)))) + sclasses immediate-join-classes sels immediate-joins instances))) (if (and flatp (= (length sclasses) 1)) (car objects) - objects)))) + objects)))) (defun find-all (view-classes &rest args &key all set-operation distinct from where group-by having - order-by order-by-descending offset limit refresh - flatp result-types inner-join on - (database *default-database*)) + order-by offset limit refresh flatp result-types + inner-join on + (database *default-database*) + instances) "Called by SELECT to generate object query results when the View Classes VIEW-CLASSES are passed as arguments to SELECT." (declare (ignore all set-operation group-by having offset limit inner-join on) @@ -943,14 +990,17 @@ superclass of the newly-defined View Class." (remf args :flatp) (remf args :additional-fields) (remf args :result-types) + (remf args :instances) (let* ((*db-deserializing* t) (sclasses (mapcar #'find-class view-classes)) - (immediate-join-slots (mapcar #'(lambda (c) (generate-retrieval-joins-list c :immediate)) sclasses)) - (immediate-join-classes (mapcar #'(lambda (jcs) - (mapcar #'(lambda (slotdef) - (find-class (gethash :join-class (view-class-slot-db-info slotdef)))) - jcs)) - immediate-join-slots)) + (immediate-join-slots + (mapcar #'(lambda (c) (generate-retrieval-joins-list c :immediate)) sclasses)) + (immediate-join-classes + (mapcar #'(lambda (jcs) + (mapcar #'(lambda (slotdef) + (find-class (gethash :join-class (view-class-slot-db-info slotdef)))) + jcs)) + immediate-join-slots)) (immediate-join-sels (mapcar #'generate-immediate-joins-selection-list sclasses)) (sels (mapcar #'generate-selection-list sclasses)) (fullsels (apply #'append (mapcar #'append sels immediate-join-sels))) @@ -970,12 +1020,6 @@ superclass of the newly-defined View Class." (setq fullsels (append fullsels (mapcar #'(lambda (att) (cons nil att)) (listify ob)))))) - (dolist (ob (listify order-by-descending)) - (when (and ob (not (member ob (mapcar #'cdr fullsels) - :test #'ref-equal))) - (setq fullsels - (append fullsels (mapcar #'(lambda (att) (cons nil att)) - (listify ob)))))) (dolist (ob (listify distinct)) (when (and (typep ob 'sql-ident) (not (member ob (mapcar #'cdr fullsels) @@ -1008,10 +1052,22 @@ superclass of the newly-defined View Class." (list :result-types result-types) (when where (list :where where)) args))) + (instances-to-add (- (length rows) (length instances))) + (perhaps-extended-instances + (if (plusp instances-to-add) + (append instances (do ((i 0 (1+ i)) + (res nil)) + ((= i instances-to-add) res) + (push (make-list (length sclasses) :initial-element nil) res))) + instances)) (objects (mapcar - #'(lambda (r) - (build-objects r sclasses immediate-join-classes sels immediate-join-sels database refresh flatp)) - rows))) + #'(lambda (row instance) + (build-objects row sclasses immediate-join-classes sels + immediate-join-sels database refresh flatp + (if (and flatp (atom instance)) + (list instance) + instance))) + rows perhaps-extended-instances))) objects)))) (defmethod instance-refreshed ((instance standard-db-object))) @@ -1057,8 +1113,8 @@ ENABLE-SQL-READER-SYNTAX." (query-get-selections select-all-args) (cond ((select-objects target-args) - (let ((caching (getf qualifier-args :caching)) - (refresh (getf qualifier-args :refresh)) + (let ((caching (getf qualifier-args :caching t)) + (refresh (getf qualifier-args :refresh nil)) (database (or (getf qualifier-args :database) *default-database*))) (remf qualifier-args :caching) (remf qualifier-args :refresh) @@ -1071,7 +1127,9 @@ ENABLE-SQL-READER-SYNTAX." ((and cached (not refresh)) cached) ((and cached refresh) - (update-cached-results target-args qualifier-args database)) + (let ((results (apply #'find-all (append (list target-args) qualifier-args `(:instances ,cached))))) + (setf (records-cache-results target-args qualifier-args database) results) + results)) (t (let ((results (apply #'find-all target-args qualifier-args))) (setf (records-cache-results target-args qualifier-args database) results)