annotate base/database/charm/database-functions.lisp @ 318:c4e792b9b898

Add some ideas for charm constituents. Not particularly useful in its own right, but contains some possibly useful ideas related to the generalisation of db-compositions.
author Jamie Forth <j.forth@gold.ac.uk>
date Thu, 30 Sep 2010 15:35:15 +0100
parents
children
rev   line source
j@318 1 (cl:in-package #:amuse-charm)
j@318 2
j@318 3 ;;;=====================================================================
j@318 4 ;;; API for inserting and retrieving constituents
j@318 5 ;;;=====================================================================
j@318 6
j@318 7 (defun store-charm-constituent (constituent database)
j@318 8 "Given a charm-constituent, store it in the database (after first
j@318 9 checking for a previously added identical constituent - this needs
j@318 10 doing properly), and then update the database related
j@318 11 slots (identifier, owner, version, and timestamps).
j@318 12
j@318 13 FIXME: What to do about constituent start and duration slots?
j@318 14 Currently all implementations use integer time in the database, but
j@318 15 then typically convert to a real number line in Lisp, e.g. geerdes
j@318 16 divides by the MIDI timebase. So what should the start and duration
j@318 17 slots mean? Should they be the real values or the database integer
j@318 18 values? If the latter, then each implementation should provide a
j@318 19 'convert-to-integer-time' function. Or the other extreme, do we
j@318 20 specift these database columns as string so that we can store what
j@318 21 ever we want (e.g. Lisp ratios instead of forcing conversion to
j@318 22 floating point). For the moment, I'm ignoring this."
j@318 23 (unless nil ;(constituent-header-exists-p constituent database)
j@318 24 (%insert-new-constituent constituent database)
j@318 25 (format t "New constituent added: id ~A."
j@318 26 (id constituent))
j@318 27 (identifier constituent)))
j@318 28
j@318 29 (defun get-charm-constituent (constituent-identifier database)
j@318 30 (let ((header (%get-constituent-header constituent-identifier
j@318 31 database)))
j@318 32 (destructuring-bind (parent ext-properties int-properties owner
j@318 33 version creation-timestamp
j@318 34 deletion-timestamp) header
j@318 35 (make-standard-charm-constituent (%get-constituent-particles
j@318 36 constituent-identifier parent
j@318 37 database) parent
j@318 38 ext-properties int-properties
j@318 39 :constituent-identifier
j@318 40 constituent-identifier
j@318 41 :owner owner
j@318 42 :version version
j@318 43 :creation-timestamp creation-timestamp
j@318 44 :deletion-timestamp
j@318 45 deletion-timestamp))))
j@318 46
j@318 47 ;; (defun cache-charm-particles (charm-constituent)
j@318 48 ;; "This could/should also re-compute the time and duration slots. Or,
j@318 49 ;; unless we can come up with a general way of storing these values in
j@318 50 ;; the constituent headers."
j@318 51 ;; (%cache-charm-particles (implementation-package (parent-identifier
j@318 52 ;; charm-constituent))
j@318 53 ;; charm-constituent))
j@318 54
j@318 55 ;(defun delete-constituent (constituent-identifier database)
j@318 56
j@318 57
j@318 58 (defun constituent-header-exists-p (constituent database)
j@318 59 (with-slots (time interval extrinsic-properties
j@318 60 intrinsic-properties)
j@318 61 constituent
j@318 62 (let ((exists
j@318 63 (car
j@318 64 (clsql:query (format nil "
j@318 65 SELECT constituent_header_exists(~S, ~S, '~A', '~A')"
j@318 66 time
j@318 67 interval
j@318 68 (object->db-string
j@318 69 extrinsic-properties)
j@318 70 (object->db-string
j@318 71 intrinsic-properties))
j@318 72 :database database
j@318 73 :flatp t
j@318 74 :field-names nil))))
j@318 75 (if (eq exists 1)
j@318 76 (progn (setf exists t)
j@318 77 (format t "Constituent header exists.~%"))
j@318 78 (progn (setf exists nil)
j@318 79 (format t "Constituent header does not exist.~%")))
j@318 80 exists)))
j@318 81
j@318 82 ;(defun constituent-particle-list-exists-p (constituent) nil)
j@318 83
j@318 84
j@318 85 ;;;=====================================================================
j@318 86 ;;; Helper functions
j@318 87 ;;;=====================================================================
j@318 88
j@318 89 (defun %insert-new-constituent (constituent database)
j@318 90 "Constituent-identifier, owner, version, and timestamps are added as
j@318 91 side effects."
j@318 92 (clsql:with-transaction (:database database)
j@318 93 (%insert-constituent-header constituent database)
j@318 94 (%insert-particles constituent database)))
j@318 95
j@318 96 (defun %insert-constituent-header (constituent database)
j@318 97 (with-slots (parent extrinsic-properties intrinsic-properties)
j@318 98 constituent
j@318 99 (clsql:execute-command (concatenate 'string "
j@318 100 INSERT INTO charm_constituent_headers SET
j@318 101 implementation_id := (SELECT get_impl_id('" (implementation-namestring
j@318 102 parent) "')),
j@318 103 parent_id := " (princ-to-string (id parent)) ",
j@318 104 ext_properties := '" (object->db-string extrinsic-properties) "',
j@318 105 int_properties := '" (object->db-string intrinsic-properties) "';")
j@318 106 :database database)
j@318 107 (%update-header-slots constituent database)))
j@318 108
j@318 109 (defun %update-header-slots (constituent database)
j@318 110 (let ((db-row-data (clsql:query "
j@318 111 SELECT last_insert_id(), owner, version, creation_timestamp, deletion_timestamp
j@318 112 FROM charm_constituent_headers
j@318 113 WHERE constituent_id = last_insert_id();"
j@318 114 :database database
j@318 115 :flatp t
j@318 116 :field-names nil)))
j@318 117 (destructuring-bind (const-id own ver create delete) (car db-row-data)
j@318 118 (setf (identifier constituent) (make-charm-constituent-identifier
j@318 119 const-id)
j@318 120 (owner constituent) own
j@318 121 (version constituent) ver
j@318 122 (creation-timestamp constituent) create
j@318 123 (deletion-timestamp constituent) delete)))
j@318 124 constituent)
j@318 125
j@318 126 (defgeneric %insert-particles (constituent database))
j@318 127
j@318 128 (defmethod %insert-particles ((constituent standard-charm-event-constituent)
j@318 129 database)
j@318 130 (if (write-particles constituent)
j@318 131 (clsql:execute-command
j@318 132 "LOAD DATA LOCAL INFILE '/tmp/particles'
j@318 133 INTO TABLE charm_constituent_particles"
j@318 134 :database database)
j@318 135 (error "file not written"))
j@318 136 (delete-file "/tmp/particles"))
j@318 137
j@318 138 (defmethod %insert-particles ((constituent standard-charm-constituent)
j@318 139 database)
j@318 140 (sequence:dosequence (particle constituent t)
j@318 141 (store-charm-constituent particle database)))
j@318 142
j@318 143 (defun write-particles (constituent)
j@318 144 (with-open-file (particle-stream (pathname "/tmp/particles")
j@318 145 :direction :output
j@318 146 :if-exists :supersede)
j@318 147 (loop for particle in (%list-slot-sequence-data constituent)
j@318 148 do (write-sequence (concatenate
j@318 149 'string ;;FIXME SET @constituent_id server side?
j@318 150 (princ-to-string (id constituent))
j@318 151 '(#\tab)
j@318 152 (princ-to-string (id particle))
j@318 153 '(#\tab)
j@318 154 (object->db-string (identifier particle)) ; type
j@318 155 '(#\tab)
j@318 156 "1" ; version - defaults to 1
j@318 157 '(#\nl))
j@318 158 particle-stream)
j@318 159 finally (return t))))
j@318 160
j@318 161
j@318 162 ;;;=====================================================================
j@318 163 ;;; Retrieving
j@318 164 ;;;=====================================================================
j@318 165
j@318 166 (defun %get-constituent-header (constituent-identifier database)
j@318 167 "Basic low-level retrieval of constituents. Just takes an identifier
j@318 168 and returns a header without any checking of version or deletion
j@318 169 fields."
j@318 170 (let ((header-row (clsql:query (concatenate 'string "
j@318 171 SELECT implementation_name, parent_id, ext_properties, int_properties,
j@318 172 owner, version, creation_timestamp, deletion_timestamp
j@318 173 FROM charm_constituent_headers
j@318 174 LEFT JOIN amuse_implementations
j@318 175 USING (implementation_id)
j@318 176 WHERE constituent_id = " (princ-to-string (id constituent-identifier)))
j@318 177 :flatp t
j@318 178 :field-names nil
j@318 179 :database database)))
j@318 180 (%init-header (car header-row))))
j@318 181
j@318 182 (defun %init-header (header-row)
j@318 183 (destructuring-bind (impl-name parent ext-properties int-properties
j@318 184 owner version creation-timestamp
j@318 185 deletion-timestamp) header-row
j@318 186 (setf ext-properties (make-charm-property-list (read-from-string
j@318 187 ext-properties)))
j@318 188 (setf int-properties (make-charm-property-list (read-from-string
j@318 189 int-properties)))
j@318 190 (setf impl-name (find-package impl-name))
j@318 191 (setf parent
j@318 192 (get-charm-parent
j@318 193 (make-charm-parent-identifier impl-name parent)))
j@318 194 ;; FIXME: probable should turn timestamps into objects
j@318 195 (list parent ext-properties int-properties owner version
j@318 196 creation-timestamp deletion-timestamp)))
j@318 197
j@318 198 (defun %get-constituent-particles (constituent-identifier parent
j@318 199 database)
j@318 200 (let ((particle-rows (clsql:query (concatenate 'string "
j@318 201 SELECT particle_id, version_added, version_removed
j@318 202 FROM charm_constituent_particles
j@318 203 WHERE constituent_id = " (princ-to-string
j@318 204 (id constituent-identifier)))
j@318 205 :database database
j@318 206 :flatp t
j@318 207 :field-names nil)))
j@318 208 (%init-particle-rows particle-rows parent)))
j@318 209
j@318 210 ;; (defun %init-particle-rows (particle-rows impl-package)
j@318 211 ;; (loop for row in particle-rows
j@318 212 ;; collect (make-charm-particle-identifier impl-package (car row))
j@318 213 ;; into particles
j@318 214 ;; finally (return particles)))
j@318 215
j@318 216 (defun %init-particle-rows (particle-rows parent)
j@318 217 (select-events-by-ids parent
j@318 218 (loop for row in particle-rows
j@318 219 collect (car row)
j@318 220 into ids
j@318 221 finally (return ids))))