r8946: merge done except for changes in objects file
[clsql.git] / sql / metaclasses.lisp
index 9d2924ab46b56431f1ed8faa84c736c71a8340cb..34e6c696cd9e1a8af59be50d93989abc51e7fe0d 100644 (file)
@@ -1,15 +1,16 @@
 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
-;;;; ======================================================================
-;;;; $Id: $
-;;;; ======================================================================
+;;;; *************************************************************************
 ;;;;
-;;;; Description ==========================================================
-;;;; ======================================================================
+;;;; $Id$
 ;;;;
 ;;;; CLSQL metaclass for standard-db-objects created in the OODDL. 
 ;;;;
-;;;; ======================================================================
-
+;;;; This file is part of CLSQL.
+;;;;
+;;;; CLSQL users are granted the rights to distribute and use this software
+;;;; as governed by the terms of the Lisp Lesser GNU Public License
+;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
+;;;; *************************************************************************
 
 (in-package #:clsql-sys)
 
@@ -36,7 +37,7 @@
 ;; ------------------------------------------------------------
 ;; metaclass: view-class
 
-(defclass standard-db-class (standard-class)
+(defclass view-metaclass (standard-class)
   ((view-table
     :accessor view-table
     :initarg :view-table)
@@ -90,7 +91,7 @@
 
 #+lispworks 
 (defmethod clos::canonicalize-defclass-slot :around
-  ((prototype standard-db-class) slot)
+  ((prototype view-metaclass) slot)
  "\\lw\\ signals an error on unknown slot options; so this method
 removes any extra allowed options before calling the default method
 and returns the canonicalized extra options concatenated to the result
@@ -120,7 +121,7 @@ of the default method.  The extra allowed options are the value of the
 
 #+lispworks 
 (defmethod clos::canonicalize-class-options :around
-    ((prototype standard-db-class) class-options)
+    ((prototype view-metaclass) class-options)
   "\\lw\\ signals an error on unknown class options; so this method
 removes any extra allowed options before calling the default method
 and returns the canonicalized extra options concatenated to the result
@@ -144,7 +145,7 @@ of the default method.  The extra allowed options are the value of the
     result))
 
 
-(defmethod validate-superclass ((class standard-db-class)
+(defmethod validate-superclass ((class view-metaclass)
                                (superclass standard-class))
   t)
 
@@ -177,13 +178,13 @@ of the default method.  The extra allowed options are the value of the
       (pop-arg mylist))
     newlist))
 
-(defmethod initialize-instance :around ((class standard-db-class)
+(defmethod initialize-instance :around ((class view-metaclass)
                                         &rest all-keys
                                        &key direct-superclasses base-table
                                         schemas version qualifier
                                        &allow-other-keys)
   (let ((root-class (find-class 'standard-db-object nil))
-       (vmc (find-class 'standard-db-class)))
+       (vmc (find-class 'view-metaclass)))
     (setf (view-class-qualifier class)
           (car qualifier))
     (if root-class
@@ -209,13 +210,13 @@ of the default method.  The extra allowed options are the value of the
     (register-metaclass class (nth (1+ (position :direct-slots all-keys))
                                    all-keys))))
 
-(defmethod reinitialize-instance :around ((class standard-db-class)
+(defmethod reinitialize-instance :around ((class view-metaclass)
                                           &rest all-keys
                                           &key base-table schemas version
                                           direct-superclasses qualifier
                                           &allow-other-keys)
   (let ((root-class (find-class 'standard-db-object nil))
-       (vmc (find-class 'standard-db-class)))
+       (vmc (find-class 'view-metaclass)))
     (setf (view-table class)
           (table-name-from-arg (sql-escape (or (and base-table
                                                     (if (listp base-table)
@@ -261,7 +262,7 @@ of the default method.  The extra allowed options are the value of the
                    (slot-value slot 'db-kind)
                    (and (slot-boundp slot 'column)
                         (slot-value slot 'column))))))
-    (let ((all-slots (mapcar #'frob-slot (class-slots class))))
+    (let ((all-slots (mapcar #'frob-slot (ordered-class-slots class))))
       (setq all-slots (remove-if #'not-db-col all-slots))
       (setq all-slots (stable-sort all-slots #'string< :key #'car))
       ;;(mapcar #'dink-type all-slots)
@@ -280,14 +281,23 @@ of the default method.  The extra allowed options are the value of the
     (setf (key-slots class) (remove-if-not (lambda (slot)
                                             (eql (slot-value slot 'db-kind)
                                                  :key))
-                                          (class-slots class)))))
+                                          (ordered-class-slots class)))))
 
 #+(or allegro openmcl)
-(defmethod finalize-inheritance :after ((class standard-db-class))
+(defmethod finalize-inheritance :after ((class view-metaclass))
+  ;; KMRL for slots without a type set, openmcl sets type-predicate to ccl:false
+  ;; for view-metaclass
+  #+openmcl
+  (mapcar 
+   #'(lambda (s)
+       (if (eq 'ccl:false (slot-value s 'ccl::type-predicate))
+          (setf (slot-value s 'ccl::type-predicate) 'ccl:true)))
+   (class-slots class))
+
   (setf (key-slots class) (remove-if-not (lambda (slot)
                                           (eql (slot-value slot 'db-kind)
                                                :key))
-                                        (class-slots class))))
+                                        (ordered-class-slots class))))
 
 ;; return the deepest view-class ancestor for a given view class
 
@@ -374,7 +384,11 @@ all NULL values retrieved are converted by DATABASE-NULL-VALUE")
    (db-info
     :accessor view-class-slot-db-info
     :initarg :db-info
-    :documentation "Description of the join.")))
+    :documentation "Description of the join.")
+   (specified-type
+    :accessor specified-type
+    :initform nil
+    :documentation "KMR: Internal slot storing the :type specified by user.")))
 
 (defparameter *db-info-lambda-list*
   '(&key join-class
@@ -423,26 +437,80 @@ all NULL values retrieved are converted by DATABASE-NULL-VALUE")
                                                standard-effective-slot-definition)
   ())
 
-(defmethod direct-slot-definition-class ((class standard-db-class)
+(defmethod direct-slot-definition-class ((class view-metaclass)
                                          #+kmr-normal-dsdc &rest
                                          initargs)
   (declare (ignore initargs))
   (find-class 'view-class-direct-slot-definition))
 
-(defmethod effective-slot-definition-class ((class standard-db-class)
+(defmethod effective-slot-definition-class ((class view-metaclass)
                                            #+kmr-normal-esdc &rest
                                            initargs)
   (declare (ignore initargs))
   (find-class 'view-class-effective-slot-definition))
 
+#+openmcl
+(defun compute-class-precedence-list (class)
+  ;; safe to call this in openmcl
+  (class-precedence-list class))
+
+#-(or sbcl cmu)
+(defmethod compute-slots ((class view-metaclass))
+  "Need to sort order of class slots so they are the same across
+implementations."
+  (let ((slots (call-next-method))
+       desired-sequence
+       output-slots)
+    (dolist (c (compute-class-precedence-list class))
+      (dolist (s (class-direct-slots c))
+       (let ((name (slot-definition-name s)))
+         (unless (find name desired-sequence)
+           (push name desired-sequence)))))
+    (dolist (desired desired-sequence)
+      (let ((slot (find desired slots :key #'slot-definition-name)))
+       (assert slot)
+       (push slot output-slots)))
+    output-slots))
+
+(defun compute-lisp-type-from-slot-specification (slotd specified-type)
+  "Computes the Lisp type for a user-specified type. Needed for OpenMCL
+which does type checking before storing a value in a slot."
+  #-openmcl (declare (ignore slotd))
+  ;; This function is called after the base compute-effective-slots is called.
+  ;; OpenMCL sets the type-predicate based on the initial value of the slots type.
+  ;; so we have to override the type-predicates here
+  (cond
+    ((consp specified-type)
+     (cond
+       ((and (symbolp (car specified-type))
+            (string-equal (symbol-name (car specified-type)) "string"))
+       #+openmcl (setf (slot-value slotd 'ccl::type-predicate) 'stringp)
+       'string)
+       (t
+       #+openmcl (setf (slot-value slotd 'ccl::type-predicate) 'ccl:true)
+       specified-type)))
+    #+openmcl
+    ((null specified-type)
+     ;; setting this here is not enough since openmcl later sets the
+     ;; type-predicate to ccl:false. So, have to check slots again
+     ;; in finalize-inheritance 
+     #+openmcl (setf (slot-value slotd 'ccl::type-predicate) 'ccl:true)
+     t)
+    (t
+     ;; This can be improved for OpenMCL to set a more specific type
+     ;; predicate based on the value specified-type 
+     #+openmcl (setf (slot-value slotd 'ccl::type-predicate) 'ccl:true)
+     specified-type)))
+
 ;; Compute the slot definition for slots in a view-class.  Figures out
 ;; what kind of database value (if any) is stored there, generates and
 ;; verifies the column name.
 
-(defmethod compute-effective-slot-definition ((class standard-db-class)
+(defmethod compute-effective-slot-definition ((class view-metaclass)
                                              #+kmr-normal-cesd slot-name
                                              direct-slots)
   #+kmr-normal-cesd (declare (ignore slot-name))
+
   (let ((slotd (call-next-method))
        (sd (car direct-slots)))
     
@@ -462,7 +530,6 @@ all NULL values retrieved are converted by DATABASE-NULL-VALUE")
              (when (slot-boundp sd 'db-type)
                (view-class-slot-db-type sd)))
        
-
        (setf (slot-value slotd 'nulls-ok)
              (view-class-slot-nulls-ok sd))
        
@@ -481,7 +548,6 @@ all NULL values retrieved are converted by DATABASE-NULL-VALUE")
              (when (slot-boundp sd 'db-constraints)
                (view-class-slot-db-constraints sd)))
                
-       
        ;; I wonder if this slot option and the previous could be merged,
        ;; so that :base and :key remain keyword options, but :db-kind
        ;; :join becomes :db-kind (:join <db info .... >)?
@@ -490,7 +556,16 @@ all NULL values retrieved are converted by DATABASE-NULL-VALUE")
              (when (slot-boundp sd 'db-info)
                (if (listp (view-class-slot-db-info sd))
                    (parse-db-info (view-class-slot-db-info sd))
-                   (view-class-slot-db-info sd)))))
+                   (view-class-slot-db-info sd))))
+
+       ;; KMR: store the user-specified type and then compute
+       ;; real Lisp type and store it
+       (setf (specified-type slotd)
+            (slot-definition-type slotd))
+       (setf (slot-value slotd 'type)
+            (compute-lisp-type-from-slot-specification 
+             slotd (slot-definition-type slotd)))
+       )
       ;; all other slots
       (t
        (change-class slotd 'view-class-effective-slot-definition