1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
|
(in-package #:containers)
(export '(add-index
delete-index
insert-record
delete-record
update-record
update-index
table-named
table-names
lookup-record
find-record-if
column-names
table-container
empty-tables!))
#+Ignore
(declaim (optimize (debug 3)))
;;; biassociative-container
;;;
;;; only good for 1-1 maps at this point
(defclass* biassociative-container (concrete-container
key-value-iteratable-container-mixin
initial-element-mixin
biassociative-container-mixin
test-container-mixin
)
((contents-> :unbound r)
(contents<- :unbound r))
(:default-initargs
:test #'eq))
(defmethod initialize-instance :after ((object biassociative-container) &key test)
(setf (slot-value object 'contents->)
(make-container 'simple-associative-container :test test)
(slot-value object 'contents<-)
(make-container 'simple-associative-container :test test)))
(defmethod size ((container biassociative-container))
(size (contents-> container)))
(defmethod empty! ((container biassociative-container))
(empty! (contents-> container))
(empty! (contents<- container))
(values))
(defmethod iterate-elements ((container biassociative-container) fn)
(iterate-elements (contents-> container) fn))
(defmethod iterate-keys ((container biassociative-container) fn)
(iterate-elements (contents<- container) fn))
(defmethod iterate-key-value ((container biassociative-container) function)
(iterate-key-value (contents-> container) function))
(defmethod iterate-value-key ((container biassociative-container) function)
(iterate-key-value (contents<- container) function))
(defmethod item-at ((container biassociative-container) &rest indexes)
(declare (dynamic-extent indexes))
(item-at-1 (contents-> container) (first indexes)))
(defmethod item-at! ((container biassociative-container) value &rest indexes)
(setf (item-at-1 (contents-> container) (first indexes)) value
(item-at-1 (contents<- container) value) (first indexes)))
(defmethod key-at ((container biassociative-container) value)
(item-at-1 (contents<- container) value))
#+Test
(let ((c (make-container 'biassociative-container)))
(setf (item-at c :a) 1)
(setf (item-at c :b) 2)
(iterate-keys c #'print)
(iterate-elements c #'print)
(print (key-at c 2))
(print (key-at c 1)))
;;; table-container
(defclass* table-container (key-value-iteratable-container-mixin
concrete-container)
((unique-counter 0 r)
(contents :unbound r)
(indexes :unbound r)
(index :unbound a)
(prototype :unbound ir)
(update-queue :unbound r)
(primary-key :unbound r)))
(defmethod print-object ((container table-container) stream)
(print-unreadable-object (container stream :type t :identity t)
(format stream "~A ~D" (class-name (prototype container)) (size container))))
(defmethod initialize-instance :after ((object table-container) &key)
(setf (slot-value object 'contents)
(make-container 'biassociative-container)
(slot-value object 'indexes)
(make-container 'alist-container)
(slot-value object 'update-queue) (make-container 'basic-queue)))
(defclass* table-index ()
((table nil ir)
(key nil ir)
(kind nil ir)
(test 'eq ir)
(index nil r)))
(defmethod initialize-instance :after ((object table-index) &key test)
(setf (slot-value object 'index)
(make-container 'simple-associative-container :test test)))
(defgeneric add-index (table name index-kind function test)
(:documentation ""))
(defgeneric delete-index (table name)
(:documentation ""))
(defgeneric insert-record (table object)
(:documentation ""))
(defgeneric delete-record (table object)
(:documentation ""))
(defgeneric update-index (index)
(:documentation ""))
(defgeneric save-pending-updates (table)
(:documentation ""))
(defmethod add-index ((object table-container) (name symbol)
(index-kind symbol) key test)
(setf (item-at (indexes object) name)
(make-instance 'table-index :table object
:kind index-kind :key key :test test))
(when (eq index-kind :primary-key)
(setf (slot-value object 'primary-key) name))
(update-index (item-at (indexes object) name)))
(defmethod delete-index ((object table-container) (name symbol))
(delete-item-at (indexes object) name))
(defmethod update-index ((index table-index))
(let ((index-data (index index))
(index-key (key index)))
(iterate-key-value
(contents (table index))
(lambda (key element)
;;?? not right
(setf (item-at index-data (funcall index-key element)) key)))))
(defmethod update-index-for-object (index object object-id)
(setf (item-at (index index) (funcall (key index) object)) object-id))
(defmethod update-indexes-for-object (table object)
(let ((object-id (key-at (contents table) object)))
(iterate-elements (indexes table)
(lambda (index)
(update-index-for-object index object object-id)))))
(defmethod insert-record ((table table-container) object)
(setf (item-at (contents table) (incf (slot-value table 'unique-counter)))
object)
(update-indexes-for-object table object))
(defmethod find-record ((table table-container) (index-name symbol) value)
(item-at
(contents table)
(item-at (index (item-at (indexes table) index-name)) value)))
(defmethod lookup-record ((table table-container) value &optional (error? nil))
(cond ((find-record table (primary-key table) value))
((not error?) (values nil))
(t
(error 'record-not-found-error
:table table
:value value))))
(defmethod find-record-if ((table table-container) predicate &optional (error? nil))
(cond ((block searcher
(iterate-elements
table
(lambda (element)
(when (funcall predicate element)
(return-from searcher element))))))
((not error?) (values nil))
(t
;;?? this is a bit wonky
(error 'record-not-found-error
:table table
:value predicate))))
(defmethod size ((container table-container))
(size (contents container)))
(defmethod iterate-container ((container table-container) fn)
(iterate-elements (contents container) fn))
#+Test
(defclass* foo (u:numbered-instances-mixin)
((value nil ia)))
#+Test
(let ((c (make-container 'table-container)))
(add-index c 'id 'u:object-number)
(add-index c 'value 'value)
(insert-record c (make-instance 'foo :value 1))
(insert-record c (make-instance 'foo :value 2))
(insert-record c (make-instance 'foo :value 3))
c)
(defmethod column-names ((container table-container))
(mopu:slot-names (prototype container)))
(defmethod empty! ((container table-container))
(empty! (contents container))
(empty! (update-queue container))
(setf (slot-value container 'unique-counter) 0))
;;; "database"
(defclass* database-mixin ()
((database-tables :unbound r))
:export-slots
(:export-p t))
(defmethod initialize-instance :after ((object database-mixin) &key)
(setf (slot-value object 'database-tables)
(make-container
'alist-container
:initial-element-fn (lambda ()
(make-container 'associative-container)))))
(defmethod table-named ((database database-mixin) (table symbol))
(item-at (database-tables database) table))
(defmethod (setf table-named) ((value table-container)
(database database-mixin) (table symbol))
(setf (item-at (database-tables database) table) value))
(defgeneric table-names (database)
(:documentation "Returns a list of the names of the tables in a database.")
(:method ((database database-mixin))
(collect-keys (database-tables database))))
(defmethod empty! ((database database-mixin))
(empty! (database-tables database)))
(defmethod empty-tables! ((database database-mixin))
(iterate-elements
(database-tables database)
#'empty!))
;;; a bit wonky
(defun apply-filter-to-database (database filter)
(iterate-elements
(database-tables database)
(lambda (table)
(unless (member 'filtered-container-mixin
(mopu:superclasses table)
:key #'class-name)
(change-class
table
(find-matching-container-class (list (type-of table) 'filtered-container-mixin))))
(setf (element-filter table) filter))))
#+Test
(iterate-elements
(database-tables (ib::information-broker))
(lambda (table)
(change-class
table 'table-container)))
(defun print-schema (database)
(iterate-key-value
(database-tables database)
(lambda (name table)
(format t "~%~A~% ~{~A~^, ~}" name (column-names table)))))
|