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