merged changelog
authorRuss Tyndall <russ@acceleration.net>
Sun, 8 Jun 2014 19:13:39 +0000 (15:13 -0400)
committerRuss Tyndall <russ@acceleration.net>
Sun, 8 Jun 2014 19:13:39 +0000 (15:13 -0400)
ChangeLog
sql/conditions.lisp
sql/oodml.lisp
tests/ds-employees.lisp
tests/test-oodml.lisp

index 842aa271f1a153eda3f1ae62016b624fcae2db5c..99487fbdc7df2ecef1957c42659218553662ec89 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2014-04-24 Russ Tyndall <russ@acceleration.net>
+       * oodml.lisp, test-oodml.lisp Better handling of view-slots of
+       type symbol/keyword.  Better handling of printing and reading
+       bindings (per mailing list request, always read and write in base
+       10)
+
 2014-03-04  Kevin Rosenberg <kevin@rosenberg.net>
        * Version 6.5.0: New release
        * makefile.common: Check for /usr/bin/dpkg-buildflags
index 3ef94122cd93c884652c1d994490bea07eba3421..1969b962ae28728e565ea2d6a11fd03f04c49acc 100644 (file)
@@ -142,3 +142,28 @@ connection is no longer usable."))
 
 (defun signal-database-too-strange (message)
   (error 'database-too-strange :message message))
+
+
+(define-condition sql-value-conversion-error (error)
+  ((expected-type :accessor expected-type :initarg :expected-type :initform nil)
+   (value :accessor value :initarg :value :initform nil)
+   (database :accessor database :initarg :database :initform nil)))
+
+(defun error-converting-value (val type &optional (database *default-database*))
+  (restart-case 
+      (error 'sql-value-conversion-error
+             :expected-type type :value val :database database)
+    (continue ()
+      :report "Continue using the unconverted value"
+      (values val t))
+    (use-value (new-val)
+      :report "Use a different value instead of this failed conversion"
+      (values new-val t)
+      )))
+
+(defun maybe-error-converting-value
+    (new val type &optional (database *default-database*))
+  (if (typep new type)
+      new
+      (error-converting-value
+       val type database)))
index 44c3e9ec6bc97c487107657255d593e7192437d0..169fa89d70d6f0fe17e834749e9a27f337cfe2f8 100644 (file)
     (char (if args
               (format nil "CHAR(~D)" (first args))
               "CHAR(1)"))
-    ((varchar string)
+    ((varchar string symbol keyword)
      (if args
          (format nil "VARCHAR(~A)" (car args))
          (format nil "VARCHAR(~D)" *default-string-length*)))
              db-type type args database)
      (format nil "VARCHAR(~D)" *default-string-length*))))
 
-(defmethod database-output-sql-as-type (type val database db-type)
-  (declare (ignore type database db-type))
-  val)
+(defun print-readable-symbol (in &aux (*package* (find-package :keyword))
+                                 (*print-readably* t))
+  (prin1-to-string in))
 
-(defmethod database-output-sql-as-type ((type symbol) val database db-type)
+(defmethod database-output-sql-as-type
+    (type val database db-type
+     &aux
+     (*print-circle* t) (*print-array* t)
+     (*print-length* nil) (*print-base* #10r10))
   (declare (ignore database))
-  (case type ;; booleans handle null differently
-    ((boolean generalized-boolean)
+  (cond 
+    ((null type) val)
+    ((member type '(boolean generalized-boolean))
+     ;; booleans handle null differently
      (case db-type
        ;; done here so it can be done once
        ((:mssql :mysql) (if val 1 0))
        (otherwise (if val "t" "f"))))
-    (otherwise
-     ;; in all other cases if we have nil give everyone else a shot at it,
-     ;; which by default returns nil
-     (if (null val)
-         (call-next-method)
-         (case type
-           (symbol
-            (format nil "~A::~A"
-                    (package-name (symbol-package val))
-                    (symbol-name val)))
-           (keyword (symbol-name val))
-           (string val)
-           (char (etypecase val
-                   (character (write-to-string val))
-                   (string val)))
-           (float (format nil "~F" val))
-           ((list vector array)
-            (let* ((*print-circle* t)
-                   (*print-array* t)
-                   (*print-length* nil)
-                   (value (prin1-to-string val)))
-              value))
-           (otherwise (call-next-method)))))))
-
-(defmethod read-sql-value (val type database db-type
-                           &aux *read-eval*)
-  (declare (ignore database db-type))
-  ;; TODO: All the read-from-strings in here do not check that
-  ;; what we read was of the correct type, should this change?
-
-  ;; TODO: Should this case `(typep val type)=>t` be an around
-  ;; method that short ciruits?
+    ((null val)
+     (when (next-method-p)
+       (call-next-method)))
+    (t
+     (case type
+       ((or symbol keyword)
+        (print-readable-symbol val))
+       (string val)
+       (char (etypecase val
+               (character (write-to-string val))
+               (string val)))
+       (float (format nil "~F" val))
+       ((list vector array)
+        (prin1-to-string val))
+       (otherwise
+        (if (next-method-p)
+            (call-next-method)
+            val))))))
+
+
+(defmethod read-sql-value :around
+    (val type database db-type
+     ;; never eval while reading values, always read base 10
+     &aux *read-eval* (*read-base* #10r10))
+  (declare (ignore 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 symbol) database db-type
-                           ;; never eval while reading values
-                           &aux *read-eval*)
-  ;; TODO: All the read-from-strings in here do not check that
-  ;; what we read was of the correct type, should this change?
-  (unless (or (equalp "nil" val) (eql 'null val))
-    (case type
-      ((string varchar) val)
-      (char (etypecase val
-              (string (schar val 0))
-              (character val)))
-      (keyword
-       (when (< 0 (length val))
-         (intern (symbol-name-default-case val) :keyword)))
-      (symbol
-       (when (< 0 (length val))
-         (intern (symbol-name-default-case val))))
-      ((smallint mediumint bigint integer universal-time)
-       (etypecase val
-         (string (parse-integer val))
-         (number val)))
-      ((double-float float)
-       ;; ensure that whatever we got is coerced to a float of the correct
-       ;; type (eg: 1=>1.0d0)
-       (float
-        (etypecase val
-          (string (let ((*read-default-float-format*
-                          (ecase type
-                            (float 'single-float)
-                            (double-float 'double-float))))
-                    (read-from-string val)))
-          (float val))
-        (if (eql type 'double-float) 1.0d0 1.0s0)))
-      (number
-       (etypecase val
+    ;; null value or type
+    ((or (equalp "nil" val) (eql 'null val)) nil) 
+    
+    ;; no specified type or already the right type
+    ((or (null type)
+         (ignore-errors (typep val type)))
+     val)
+
+    ;; actually convert
+    (t 
+     (let ((res (handler-bind
+                    ;; all errors should be converted to sql-value-conversion-error
+                    ((error (lambda (c)
+                              (when *debugger-hook*
+                                (invoke-debugger c))
+                              (unless (typep c 'sql-value-conversion-error)
+                                (error-converting-value val type database)))))
+                  (call-next-method))))
+       ;; if we didnt get the right type after converting, we should probably
+       ;; error right away
+       (maybe-error-converting-value
+        res val type database)))))
+
+(defmethod read-sql-value (val type database db-type)
+  ;; errors, nulls and preconverted types are already handled in around
+  (typecase type
+    (symbol
+     (case type
+       ((string varchar) val)
+       (char (string (schar val 0)))
+       ((or keyword symbol)
+        (read-from-string val))
+       ((smallint mediumint bigint integer universal-time)
+        (parse-integer val))
+       ((double-float float)
+        ;; ensure that whatever we got is coerced to a float of the correct
+        ;; type (eg: 1=>1.0d0)
+        (float
+         (etypecase val
+           (string (let ((*read-default-float-format*
+                           (ecase type
+                             (float 'single-float)
+                             (double-float 'double-float))))
+                     (read-from-string val)))
+           ;; maybe wrong type of float
+           (float val)) 
+         (if (eql type 'double-float) 1.0d0 1.0s0)))
+       (number (read-from-string val))
+       ((boolean generalized-boolean)
+        (if (member val '(nil t))
+            val
+            (etypecase val
+              (string
+               (when (member val '("1" "t" "true" "y") :test #'string-equal)
+                 t))
+              (number (not (zerop val))))))
+       ((wall-time duration) (parse-timestring val))
+       (date (parse-datestring val))
+       (t (call-next-method))))
+    (t (typecase val
          (string (read-from-string val))
-         (number val)))
-      ((boolean generalized-boolean)
-       (if (member val '(nil t))
-           val
-           (etypecase val
-             (string
-              (when (member val '("1" "t" "true" "y") :test #'string-equal)
-                t))
-             (number (not (zerop val))))))
-      ((wall-time duration)
-       (parse-timestring val))
-      (date
-       (parse-datestring val))
-      (t (call-next-method)))))
+         (t (error-converting-value val type database))))))
 
 ;; ------------------------------------------------------------
 ;; Logic for 'faulting in' :join slots
index 1b1e36bafea8268f6c64e987883bdd99ba2f9e1e..26110534d1d8c9b47b866f728f8be52b37ea29e7 100644 (file)
     :db-constraints :not-null
     :type integer
     :initarg :groupid)
+   (title
+    :accessor title
+    :type symbol
+    :initarg :title)
    (first-name
     :accessor first-name
     :type (varchar 30)
                                    :emplid 1
                                    :groupid 1
                                    :married t
+                                   :title 'supplicant
                                    :height (1+ (random 1.00))
                                    :bd-utime *test-start-utime*
                                    :birthday now-time
           employee2 (make-instance 'employee
                                    :emplid 2
                                    :groupid 1
+                                   :title :adherent
                                    :height (1+ (random 1.00))
                                    :married t
                                    :bd-utime *test-start-utime*
           employee3 (make-instance 'employee
                                    :emplid 3
                                    :groupid 1
+                                   :title 'cl-user::novice
                                    :height (1+ (random 1.00))
                                    :married t
                                    :bd-utime *test-start-utime*
index 953a604a9adc693a657e2185c66ccab3ce37ecef..042fd48b6d71bb39cf8cb720ad60ae159dc76d85 100644 (file)
 (setq *rt-oodml*
       '(
 
+(deftest :oodml/read-symbol-value/1-into-this-package
+ (clsql-sys::read-sql-value
+  (clsql-sys::database-output-sql-as-type 'symbol 'clsql-tests::foo nil nil)
+  'symbol nil nil)
+ clsql-tests::foo)
+
+(deftest :oodml/read-symbol-value/2-into-another-pacakge
+ (clsql-sys::read-sql-value
+  (clsql-sys::database-output-sql-as-type 'symbol 'clsql-sys::foo nil nil)
+  'symbol nil nil)
+ clsql-sys::foo)
+
+(deftest :oodml/read-symbol-value/3-keyword
+ (clsql-sys::read-sql-value
+  (clsql-sys::database-output-sql-as-type 'keyword ':foo nil nil)
+  'keyword nil nil)
+ :foo)
+
+(deftest :oodml/read-symbol-value/4-keyword-error
+ (handler-case
+     (clsql-sys::read-sql-value
+      (clsql-sys::database-output-sql-as-type 'keyword 'foo nil nil)
+      'keyword nil nil)
+   (clsql-sys::sql-value-conversion-error (c) (declare (ignore c))
+     :error))
+ :error)
+
 (deftest :oodml/select/1
     (with-dataset *ds-employees*
       (mapcar #'(lambda (e) (slot-value e 'last-name))
              (select 'deferred-employee-address :flatp t :order-by [aaddressid] :caching nil)))
   (10 10 nil nil nil nil))
 
+(deftest :oodm/retrieval/10-slot-columns
+ (with-dataset *ds-employees*
+   (mapcar #'title
+           (select 'employee :flatp t :caching nil
+                   :where [<= [emplid] 3]
+                   :order-by `((,[emplid]  :asc)))))
+ (supplicant :adherent cl-user::novice))
+
 ;; tests update-records-from-instance
 (deftest :oodml/update-records/1
     (with-dataset *ds-employees*