r9114: fixes for list-indexes
[clsql.git] / sql / metaclasses.lisp
index 60679fb409eb08d062beb0ccb8acc5ac1520af5d..0efa327dd94e123518c96255ea9ffea797a970e2 100644 (file)
@@ -1,18 +1,18 @@
 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
-;;;; ======================================================================
-;;;; File:    metaclasses.lisp
-;;;; Updated: <04/04/2004 12:08:11 marcusp>
-;;;; ======================================================================
+;;;; *************************************************************************
 ;;;;
-;;;; Description ==========================================================
-;;;; ======================================================================
+;;;; $Id$
 ;;;;
-;;;; CLSQL-USQL metaclass for standard-db-objects created in the OODDL. 
+;;;; 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-usql-sys)
+(in-package #:clsql-sys)
 
 (eval-when (:compile-toplevel :load-toplevel :execute)
   (when (>= (length (generic-function-lambda-list
     :accessor object-definition
     :initarg :definition
     :initform nil)
-   (version
-    :accessor object-version
-    :initarg :version
-    :initform 0)
    (key-slots
     :accessor key-slots
     :initform nil)
@@ -117,7 +113,7 @@ of the default method.  The extra allowed options are the value of the
     result))
 
 #+lispworks
-(defconstant +extra-class-options+ '(:base-table :version :schemas))
+(defconstant +extra-class-options+ '(:base-table))
 
 #+lispworks 
 (defmethod clos::canonicalize-class-options :around
@@ -181,7 +177,7 @@ of the default method.  The extra allowed options are the value of the
 (defmethod initialize-instance :around ((class standard-db-class)
                                         &rest all-keys
                                        &key direct-superclasses base-table
-                                        schemas version qualifier
+                                        qualifier
                                        &allow-other-keys)
   (let ((root-class (find-class 'standard-db-object nil))
        (vmc (find-class 'standard-db-class)))
@@ -203,16 +199,12 @@ of the default method.  The extra allowed options are the value of the
                                                         (car base-table)
                                                         base-table))
                                                (class-name class)))))
-    (setf (object-version class) version)
-    (mapc (lambda (schema)
-            (pushnew (class-name class) (gethash schema *object-schemas*)))
-          (if (listp schemas) schemas (list schemas)))
     (register-metaclass class (nth (1+ (position :direct-slots all-keys))
                                    all-keys))))
 
 (defmethod reinitialize-instance :around ((class standard-db-class)
                                           &rest all-keys
-                                          &key base-table schemas version
+                                          &key base-table 
                                           direct-superclasses qualifier
                                           &allow-other-keys)
   (let ((root-class (find-class 'standard-db-object nil))
@@ -235,10 +227,6 @@ of the default method.  The extra allowed options are the value of the
                                                 direct-superclasses)
                   (remove-keyword-arg all-keys :direct-superclasses)))
         (call-next-method)))
-  (setf (object-version class) version)
-  (mapc (lambda (schema)
-          (pushnew (class-name class) (gethash schema *object-schemas*)))
-        (if (listp schemas) schemas (list schemas)))
   (register-metaclass class (nth (1+ (position :direct-slots all-keys))
                                  all-keys)))
 
@@ -262,7 +250,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)
@@ -281,14 +269,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))
+  ;; KMRL for slots without a type set, openmcl sets type-predicate to ccl:false
+  ;; for standard-db-class
+  #+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
 
@@ -375,7 +372,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
@@ -436,6 +437,59 @@ all NULL values retrieved are converted by DATABASE-NULL-VALUE")
   (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 standard-db-class))
+  "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.
@@ -444,6 +498,7 @@ all NULL values retrieved are converted by DATABASE-NULL-VALUE")
                                              #+kmr-normal-cesd slot-name
                                              direct-slots)
   #+kmr-normal-cesd (declare (ignore slot-name))
+
   (let ((slotd (call-next-method))
        (sd (car direct-slots)))
     
@@ -463,7 +518,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))
        
@@ -482,7 +536,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 .... >)?
@@ -491,7 +544,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