X-Git-Url: http://git.kpe.io/?a=blobdiff_plain;f=sql%2Foodml.lisp;h=b2f16a6d3319adef5620d745e213175122f9809f;hb=8d6b3157eb3b09316739a4a6f7b9dfc6844fa1f5;hp=9910ab484948088a569268f49b87fea8e9374a0d;hpb=a244caf265fff60cc9d00083e15951762dd7f1ca;p=clsql.git diff --git a/sql/oodml.lisp b/sql/oodml.lisp index 9910ab4..b2f16a6 100644 --- a/sql/oodml.lisp +++ b/sql/oodml.lisp @@ -1,8 +1,6 @@ ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*- ;;;; ************************************************************************* ;;;; -;;;; $Id$ -;;;; ;;;; The CLSQL Object Oriented Data Manipulation Language (OODML). ;;;; ;;;; This file is part of CLSQL. @@ -55,7 +53,7 @@ (defun generate-selection-list (vclass) (let* ((sels nil) (this-class vclass) - (slots (if (normalisedp vclass) + (slots (if (normalizedp vclass) (labels ((getdslots () (let ((sl (ordered-class-direct-slots this-class))) (cond (sl) @@ -174,8 +172,8 @@ (database *default-database*)) (let* ((database (or (view-database obj) database)) (view-class (class-of obj))) - (when (normalisedp view-class) - ;; If it's normalised, find the class that actually contains + (when (normalizedp view-class) + ;; If it's normalized, find the class that actually contains ;; the slot that's tied to the db (setf view-class (do ((this-class view-class @@ -208,6 +206,13 @@ (defmethod update-record-from-slots ((obj standard-db-object) slots &key (database *default-database*)) + (when (normalizedp (class-of obj)) + ;; FIXME: Rewrite to bundle slots for same table to be written + ;; as avpairs (like how is done for non-normalized view-classes below) + (dolist (slot slots) + (update-record-from-slot obj slot :database database)) + (return-from update-record-from-slots (values))) + (let* ((database (or (view-database obj) database)) (vct (view-table (class-of obj))) (sds (slotdefs-for-slots-with-class slots (class-of obj))) @@ -220,11 +225,14 @@ (db-value-from-slot s val database)))) sds))) (cond ((and avps (view-database obj)) - (update-records (sql-expression :table vct) - :av-pairs avps - :where (key-qualifier-for-instance - obj :database database) - :database database)) + (let ((where (key-qualifier-for-instance + obj :database database))) + (unless where + (error "update-record-from-slots: could not generate a where clause for ~a" obj)) + (update-records (sql-expression :table vct) + :av-pairs avps + :where where + :database database))) ((and avps (not (view-database obj))) (insert-records :into (sql-expression :table vct) :av-pairs avps @@ -250,20 +258,21 @@ (pk-slot (car (keyslots-for-class view-class))) (view-class-table (view-table view-class)) (pclass (car (class-direct-superclasses view-class)))) - (when (normalisedp view-class) + (when (normalizedp view-class) (setf pk (update-records-from-instance obj :database database :this-class pclass)) (when pk-slot (setf (slot-value obj (slot-definition-name pk-slot)) pk))) (let* ((slots (remove-if-not #'slot-storedp - (if (normalisedp view-class) + (if (normalizedp view-class) (ordered-class-direct-slots view-class) (ordered-class-slots view-class)))) (record-values (mapcar #'slot-value-list slots))) - (cond ((and (not (normalisedp view-class)) + + (cond ((and (not (normalizedp view-class)) (not record-values)) (error "No settable slots.")) - ((and (normalisedp view-class) + ((and (normalizedp view-class) (not record-values)) nil) ((view-database obj) @@ -277,21 +286,36 @@ (setf pk (or pk (slot-value obj (slot-definition-name pk-slot)))))) (t - (insert-records :into (sql-expression :table view-class-table) + (insert-records :into (sql-expression :table view-class-table) :av-pairs record-values :database database) - (when pk-slot - (if (or (and (listp (view-class-slot-db-constraints pk-slot)) - (member :auto-increment (view-class-slot-db-constraints pk-slot))) - (eql (view-class-slot-db-constraints pk-slot) :auto-increment)) - (setf pk (or pk - (car (query "SELECT LAST_INSERT_ID();" - :flatp t :field-names nil - :database database)))) - (setf pk (or pk - (slot-value obj (slot-definition-name pk-slot)))))) - (when (eql this-class nil) - (setf (slot-value obj 'view-database) database))))))) + + (when (and pk-slot (not pk)) + (setf pk (if (or (member :auto-increment (listify (view-class-slot-db-constraints pk-slot))) + (not (null (view-class-slot-autoincrement-sequence pk-slot)))) + (setf (slot-value obj (slot-definition-name pk-slot)) + (database-last-auto-increment-id database + view-class-table + pk-slot))))) + (when pk-slot + (setf pk (or pk + (slot-value + obj (slot-definition-name pk-slot))))) + (when (eql this-class nil) + (setf (slot-value obj 'view-database) database))))))) + ;; handle slots with defaults + (let* ((view-class (or this-class (class-of obj))) + (slots (if (normalizedp view-class) + (ordered-class-direct-slots view-class) + (ordered-class-slots view-class)))) + (dolist (slot slots) + (when (and (slot-exists-p slot 'db-constraints) + (listp (view-class-slot-db-constraints slot)) + (member :default (view-class-slot-db-constraints slot))) + (unless (and (slot-boundp obj (slot-definition-name slot)) + (slot-value obj (slot-definition-name slot))) + (update-slot-from-record obj (slot-definition-name slot)))))) + pk)) (defmethod delete-instance-records ((instance standard-db-object)) @@ -311,7 +335,7 @@ (let* ((view-class (or this-class (class-of instance))) (pclass (car (class-direct-superclasses view-class))) (pres nil)) - (when (normalisedp view-class) + (when (normalizedp view-class) (setf pres (update-instance-from-records instance :database database :this-class pclass))) (let* ((view-table (sql-expression :table (view-table view-class))) @@ -327,6 +351,7 @@ :result-types nil :database vd)))) (when res + (setf (slot-value instance 'view-database) vd) (get-slot-values-from-view instance (mapcar #'car sels) (car res)))) (pres) (t nil))))) @@ -335,8 +360,8 @@ slot &key (database *default-database*)) (let* ((view-class (find-class (class-name (class-of instance)))) (slot-def (slotdef-for-slot-with-class slot view-class))) - (when (normalisedp view-class) - ;; If it's normalised, find the class that actually contains + (when (normalizedp view-class) + ;; If it's normalized, find the class that actually contains ;; the slot that's tied to the db (setf view-class (do ((this-class view-class @@ -353,6 +378,7 @@ (res (select att-ref :from view-table :where view-qual :result-types nil))) (when res + (setf (slot-value instance 'view-database) vd) (get-slot-values-from-view instance (list slot-def) (car res)))))) (defmethod update-slot-with-null ((object standard-db-object) @@ -575,8 +601,12 @@ (format nil "~F" val)))) (defmethod read-sql-value (val type database db-type) - (declare (ignore type database db-type)) - (read-from-string val)) + (declare (ignore database db-type)) + (cond + ((null type) val) ;;we have no desired type, just give the value + ((typep val type) val) ;;check that it hasn't already been converted. + ((typep val 'string) (read-from-string val)) ;;maybe read will just take care of it? + (T (error "Unable to read-sql-value ~a as type ~a" val type)))) (defmethod read-sql-value (val (type (eql 'string)) database db-type) (declare (ignore database db-type)) @@ -630,10 +660,19 @@ (declare (ignore database db-type)) ;; writing 1.0 writes 1, so we we *really* want a float, must do (float ...) (etypecase val - (string - (float (read-from-string val))) - (float - val))) + (string (float (read-from-string val))) + (float val))) + +(defmethod read-sql-value (val (type (eql 'double-float)) database db-type) + (declare (ignore database db-type)) + ;; writing 1.0 writes 1, so if we *really* want a float, must do (float ...) + (etypecase val + (string (float + (let ((*read-default-float-format* 'double-float)) + (read-from-string val)) + 1.0d0)) + (double-float val) + (float (coerce val 'double-float)))) (defmethod read-sql-value (val (type (eql 'boolean)) database db-type) (declare (ignore database db-type)) @@ -875,7 +914,7 @@ maximum of MAX-LEN instances updated in each query." ;;;; Should we not return the whole result, instead of only ;;;; the one slot-value? We get all the values from the db ;;;; anyway, so? -(defun fault-join-normalised-slot (class object slot-def) +(defun fault-join-normalized-slot (class object slot-def) (labels ((getsc (this-class) (let ((sc (car (class-direct-superclasses this-class)))) (if (key-slots sc) @@ -898,7 +937,7 @@ maximum of MAX-LEN instances updated in each query." (slot-value object hk)) (t hk))))) - ;; Caching nil in next select, because in normalised mode + ;; Caching nil in next select, because in normalized mode ;; records can be changed through other instances (children, ;; parents) so changes possibly won't be noticed (let ((res (car (select (class-name sc) :where jq @@ -907,14 +946,14 @@ maximum of MAX-LEN instances updated in each query." :database (view-database object)))) (slot-name (slot-definition-name slot-def))) - ;; If current class is normalised and wanted slot is not + ;; If current class is normalized and wanted slot is not ;; a direct member, recurse up - (if (and (normalisedp class) + (if (and (normalizedp class) (not (member slot-name (mapcar #'(lambda (esd) (slot-definition-name esd)) (ordered-class-direct-slots class)))) (not (slot-boundp res slot-name))) - (fault-join-normalised-slot sc res slot-def) + (fault-join-normalized-slot sc res slot-def) (slot-value res slot-name)))))) ) (defun join-qualifier (class object slot-def) @@ -979,7 +1018,7 @@ maximum of MAX-LEN instances updated in each query." ;; find all immediate-select slots and join-vals for this object (let* ((jo-class (class-of jo)) (slots - (if (normalisedp jo-class) + (if (normalizedp jo-class) (class-direct-slots jo-class) (class-slots jo-class))) (pos-list (remove-if #'null @@ -1222,15 +1261,19 @@ as elements of a list." (when (and order-by (= 1 (length target-args))) (let ((table-name (view-table (find-class (car target-args)))) (order-by-list (copy-seq (listify order-by)))) - - (loop for i from 0 below (length order-by-list) - do (etypecase (nth i order-by-list) - (sql-ident-attribute - (unless (slot-value (nth i order-by-list) 'qualifier) - (setf (slot-value (nth i order-by-list) 'qualifier) table-name))) - (cons - (unless (slot-value (car (nth i order-by-list)) 'qualifier) - (setf (slot-value (car (nth i order-by-list)) 'qualifier) table-name))))) + (labels ((set-table-if-needed (val) + (typecase val + (sql-ident-attribute + (handler-case + (unless (slot-value val 'qualifier) + (setf (slot-value val 'qualifier) table-name)) + (simple-error () + ;; TODO: Check for a specific error we expect + ))) + (cons (set-table-if-needed (car val)))))) + (loop for i from 0 below (length order-by-list) + for id = (nth i order-by-list) + do (set-table-if-needed id))) (setf (getf qualifier-args :order-by) order-by-list))) (cond