(:metaclass standard-db-class)
(:documentation "Superclass for all CLSQL View Classes."))
+(defvar *db-auto-sync* nil
+ "A non-nil value means that creating View Class instances or
+ setting their slots automatically creates/updates the
+ corresponding records in the underlying database.")
+
(defvar *db-deserializing* nil)
(defvar *db-initializing* nil)
(call-next-method))
(defmethod (setf slot-value-using-class) (new-value (class standard-db-class)
- instance slot)
- (declare (ignore new-value instance slot))
- (call-next-method))
+ instance slot-def)
+ (declare (ignore new-value))
+ (let ((slot-name (%svuc-slot-name slot-def))
+ (slot-kind (view-class-slot-db-kind slot-def)))
+ (call-next-method)
+ (when (and *db-auto-sync*
+ (not *db-initializing*)
+ (not *db-deserializing*)
+ (not (eql slot-kind :virtual)))
+ (update-record-from-slot instance slot-name))))
-(defmethod initialize-instance :around ((object standard-db-object)
+(defmethod initialize-instance ((object standard-db-object)
&rest all-keys &key &allow-other-keys)
(declare (ignore all-keys))
(let ((*db-initializing* t))
(call-next-method)
- (unless *db-deserializing*
- #+nil (created-object object)
+ (when (and *db-auto-sync*
+ (not *db-deserializing*))
(update-records-from-instance object))))
;;
;; Called by 'get-slot-values-from-view'
;;
-(declaim (inline delistify))
-(defun delistify (list)
- (if (listp list)
- (car list)
- list))
(defvar *update-context* nil)
;; ------------------------------------------------------------
;; Logic for 'faulting in' :join slots
+;; this works, but is inefficient requiring (+ 1 n-rows)
+;; SQL queries
+#+ignore
+(defun fault-join-target-slot (class object slot-def)
+ (let* ((res (fault-join-slot-raw class object slot-def))
+ (dbi (view-class-slot-db-info slot-def))
+ (target-name (gethash :target-slot dbi))
+ (target-class (find-class target-name)))
+ (when res
+ (mapcar (lambda (obj)
+ (list
+ (car
+ (fault-join-slot-raw
+ target-class
+ obj
+ (find target-name (class-slots (class-of obj))
+ :key #'slot-definition-name)))
+ obj))
+ res)
+ #+ignore ;; this doesn't work when attempting to call slot-value
+ (mapcar (lambda (obj)
+ (cons obj (slot-value obj ts))) res))))
+
+(defun fault-join-target-slot (class object slot-def)
+ (let* ((dbi (view-class-slot-db-info slot-def))
+ (ts (gethash :target-slot dbi))
+ (jc (gethash :join-class dbi))
+ (ts-view-table (view-table (find-class ts)))
+ (jc-view-table (view-table (find-class jc)))
+ (tdbi (view-class-slot-db-info
+ (find ts (class-slots (find-class jc))
+ :key #'slot-definition-name)))
+ (retrieval (gethash :retrieval tdbi))
+ (jq (join-qualifier class object slot-def))
+ (key (slot-value object (gethash :home-key dbi))))
+ (when jq
+ (ecase retrieval
+ (:immediate
+ (let ((res
+ (find-all (list ts)
+ :inner-join (sql-expression :table jc-view-table)
+ :on (sql-operation
+ '==
+ (sql-expression
+ :attribute (gethash :foreign-key tdbi)
+ :table ts-view-table)
+ (sql-expression
+ :attribute (gethash :home-key tdbi)
+ :table jc-view-table))
+ :where jq
+ :result-types :auto)))
+ (mapcar #'(lambda (i)
+ (let* ((instance (car i))
+ (jcc (make-instance jc :view-database (view-database instance))))
+ (setf (slot-value jcc (gethash :foreign-key dbi))
+ key)
+ (setf (slot-value jcc (gethash :home-key tdbi))
+ (slot-value instance (gethash :foreign-key tdbi)))
+ (list instance jcc)))
+ res)))
+ (:deferred
+ ;; just fill in minimal slots
+ (mapcar
+ #'(lambda (k)
+ (let ((instance (make-instance ts :view-database (view-database object)))
+ (jcc (make-instance jc :view-database (view-database object)))
+ (fk (car k)))
+ (setf (slot-value instance (gethash :home-key tdbi)) fk)
+ (setf (slot-value jcc (gethash :foreign-key dbi))
+ key)
+ (setf (slot-value jcc (gethash :home-key tdbi))
+ fk)
+ (list instance jcc)))
+ (select (sql-expression :attribute (gethash :foreign-key tdbi) :table jc-view-table)
+ :from (sql-expression :table jc-view-table)
+ :where jq)))))))
+
+(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."
+ (when objects
+ (unless class-name
+ (class-name (class-of (first objects))))
+ )
+ )
+
+
(defun fault-join-slot-raw (class object slot-def)
(let* ((dbi (view-class-slot-db-info slot-def))
(jc (gethash :join-class dbi)))
(defun fault-join-slot (class object slot-def)
(let* ((dbi (view-class-slot-db-info slot-def))
- (ts (gethash :target-slot dbi))
- (res (fault-join-slot-raw class object slot-def)))
- (when res
- (cond
- ((and ts (gethash :set dbi))
- (mapcar (lambda (obj)
- (cons obj (slot-value obj ts))) res))
- ((and ts (not (gethash :set dbi)))
- (mapcar (lambda (obj) (slot-value obj ts)) res))
- ((and (not ts) (not (gethash :set dbi)))
- (car res))
- ((and (not ts) (gethash :set dbi))
- res)))))
+ (ts (gethash :target-slot dbi)))
+ (if (and ts (gethash :set dbi))
+ (fault-join-target-slot class object slot-def)
+ (let ((res (fault-join-slot-raw class object slot-def)))
+ (when res
+ (cond
+ ((and ts (not (gethash :set dbi)))
+ (mapcar (lambda (obj) (slot-value obj ts)) res))
+ ((and (not ts) (not (gethash :set dbi)))
+ (car res))
+ ((and (not ts) (gethash :set dbi))
+ res)))))))
(defun join-qualifier (class object slot-def)
(declare (ignore class))
(apply #'sql-and jc)
jc))))))
-(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 (database *default-database*))
+(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*))
"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)
+ (declare (ignore all set-operation group-by having offset limit inner-join on)
(optimize (debug 3) (speed 1)))
(remf args :from)
(remf args :flatp)
+ (remf args :additional-fields)
(remf args :result-types)
(labels ((table-sql-expr (table)
(sql-expression :table (view-table table)))
(let* ((class-name (class-name vclass))
(db-vals (butlast vals (- (list-length vals)
(list-length selects))))
- (*db-initializing* t)
(obj (make-instance class-name :view-database database)))
;; use refresh keyword here
(setf obj (get-slot-values-from-view obj (mapcar #'car selects)
(car objects)
objects))))
(let* ((*db-deserializing* t)
- (*default-database* (or database
- (error 'clsql-base::clsql-no-database-error :database nil)))
(sclasses (mapcar #'find-class view-classes))
(sels (mapcar #'generate-selection-list sclasses))
(fullsels (apply #'append sels))
(cons :from
(list (append (when from (listify from))
(listify tables))))
- (list :result-types nil)
+ (list :result-types result-types)
args)))
(mapcar #'(lambda (r) (build-objects r sclasses sels)) res))))
(multiple-value-bind (target-args qualifier-args)
(query-get-selections select-all-args)
(if (select-objects target-args)
- (apply #'find-all target-args qualifier-args)
+ (apply #'find-all target-args qualifier-args)
(let* ((expr (apply #'make-query select-all-args))
(specified-types
(mapcar #'(lambda (attrib)