Skip to content

Instantly share code, notes, and snippets.

@mon-key
Created September 20, 2012 22:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mon-key/3758846 to your computer and use it in GitHub Desktop.
Save mon-key/3758846 to your computer and use it in GitHub Desktop.
Some CL for examining the SQLite dbs dumpbed by MyStuff2
;;; :FILE-CREATED <Timestamp: #{2012-06-05T14:40:40-04:00Z}#{12232} - by MON>
;;; :FILE mystuff2-model/mystuff2-sqlite.lisp
;;; ==============================
#|
As of 2012-09-20 unverified with version Mystuff2 v2.0
Known to work with MyStuff2 version 1.4x with the following CL implementation:
(concatenate 'string (lisp-implementation-type) " " (lisp-implementation-version))
=> "SBCL 1.0.55.4-eee9d17"
The commented examples below and in the doc-strings assume that the connected
database has a collection named "fashion-inventory" which contains the
categories "show-inventory-wearable" and "show-inventory-buttons", each of which
have an attribute named ":inventory-number" the values of which acts as a
``primary-key'' for these categories.
---
Usage:
Make sure CL sqlite package is loaded into our system:
(quicklisp:quickload 'sqlite)
(defpackage #:mystuff2-model (:use #:common-lisp))
(in-package #:mystuff2-model)
---
To initialize the sqlite database connection assign a path for
`*mystuff2-sqlite-db-pathname*' with `set-mystuff2-sqlite-db-pathname' then
establish an sqlite:sqlite-handle by connecting to the db at the path with
`connect-mystuff2-db'.
:EXAMPLE
(connect-mystuff2-db
(set-mystuff2-sqlite-db-pathname #P"/<SOME-PATH-TO>/mystuff.db"))
We can now prepare statements for executing SQL queries with the handle.
Disconnect the sqlite:sqlite-handle with `disconnect-mystuff2-db'.
|#
(in-package #:mystuff2-model)
(defparameter *mystuff2-sqlite-db-connection* nil)
(defparameter *mystuff2-sqlite-db-pathname* nil
"A pathname to a MyStuff2 sqlite database file.
Normally these have the `cl:file-namestring':
\"mystuff.db\"
:SEE-ALSO `set-mystuff2-sqlite-db-pathname', `*mystuff2-sqlite-db-pathname*'.~%▶▶▶")
(defun set-mystuff2-sqlite-db-pathname (database-pathname)
"Set value of `*mystuff2-sqlite-db-pathname*' to pathname DATABASE-PATHNAME.
An error is signalled if DATABASE-PATHNAME does not satisfy `cl:probe-file'.
:EXAMPLE
\(set-mystuff2-sqlite-db-pathname #P\"/<some-path-to>/mystuff.db\"\)"
(declare (pathname database-pathname))
(setf *mystuff2-sqlite-db-pathname*
(or (probe-file database-pathname)
(error ":FUNCTION `set-mystuff2-sqlite-database-pathname' -- arg DATABASE-PATHNAME does not satisfy `cl:probe-file'~% got: ~S"
database-pathname))))
;; :memory is the temporary in-memory database
(defun connect-mystuff2-db (&key (database-pathname *mystuff2-sqlite-db-pathname*))
"Connect to the sqlite database at DATABASE-PATHNAME and return an object of
type `sqlite:sqlite-handle'.
An error is signalled if DATABASE-PATHNAME does not satisfy `cl:probe-file'.
:EXAMPLE
\(connect-mystuff2-db :database-pathname *mystuff2-sqlite-db-pathname*\)"
(declare (pathname database-pathname))
(setf *mystuff2-sqlite-db-connection*
(sqlite:connect
(or (probe-file database-pathname)
(error ":FUNCTION `connect-mystuff2-db' -- arg DATABASE-PATHNAME does not satisfy `cl:probe-file'~% got: ~S"
database-pathname)))))
(defun disconnect-mystuff2-db (&optional (db-handle *mystuff2-sqlite-db-connection*))
"Disconnect the handle to database specified by DB-HANDLE and return as values
DB-HANDLE and a boolean indicating whether the handle was disconnected.
DB-HANDLE is an object of type `sqlite:sqlite-handle'.
If the sqlite::handle slot is `cl:slot-boundp' it is disconnected as if by
`sqlite:disconnect' else no action is performed on DB-HANDLE.
:EXAMPLE
\(disconnect-mystuff2-db *mystuff2-sqlite-db-connection*\)"
(declare (sqlite:sqlite-handle db-handle))
(check-type *mystuff2-sqlite-db-connection* sqlite:sqlite-handle)
(if (and (slot-exists-p db-handle 'sqlite::handle)
(slot-boundp db-handle 'sqlite::handle))
(values (sqlite:disconnect db-handle) t)
(values db-handle nil)))
;; (execute-to-vector *mystuff2-sqlite-db-connection* "SELECT item_value.text_value FROM item_value;")
(defun execute-to-vector (db sql &rest parameters)
"Executes the query SQL to the database DB with given PARAMETERS.
Like sqlite:execute-to-list but return value is a a vector of lists.
Example:
\(execute-to-vector db \"select id, user_name, real_name from users where user_name = ?\" \"joe\")
=>
\#((1 \"joe\" \"Joe the User\")
(2 \"joe\" \"Another Joe\"))
See BIND-PARAMETER for the list of supported parameter types."
(declare (dynamic-extent parameters))
(sqlite::with-prepared-statement var-statement (db sql parameters)
(let ((result (make-array 0 :fill-pointer 0)))
(loop
(if (sqlite:step-statement var-statement)
(vector-push-extend
(iter:iter (iter:for i iter::from 0 iter::below (the fixnum (sqlite-ffi:sqlite3-column-count (sqlite::handle var-statement))))
(declare (type fixnum i))
(iter:collect (sqlite:statement-column-value var-statement i)))
result)
(return)))
result)))
;; (execute-to-vector *mystuff2-sqlite-db-connection* "select * from item_value where attribute_id = ?" "352758417769")
;; (category-name-to-id "show-inventory-wearable") => 355763820224
;; (category-name-to-id "show-inventory-buttons") => 355763818823
;; SELECT `category_id` FROM `category` WHERE `name` = <STRING>;
(defun category-name-to-id (category-name
&key (db-connection *mystuff2-sqlite-db-connection*))
(declare (mon:string-not-empty category-name))
(sqlite:execute-single db-connection
"SELECT category_id
FROM category
WHERE name = ?" category-name))
;; (category-id-to-name 355763818823) => "show-inventory-buttons"
;; (category-id-to-name (category-name-to-id "show-inventory-buttons")) ;=> "show-inventory-buttons"
;; SELECT `name` FROM `category` WHERE `category_id` = <INTEGER>;
(defun category-id-to-name (category-id
&key (db-connection *mystuff2-sqlite-db-connection*))
;; (declare (integer category-id))
(sqlite:execute-single db-connection
"SELECT name
FROM category
WHERE category_id = ?" category-id))
;; (attribute-id-category-id 359608984821) => 359588954972
;; (category-id-to-name 359588954972)
;; broken b/c the parent_id might also be an action_phase
(defun attribute-id-category-id (attribute-id &key (db-connection *mystuff2-sqlite-db-connection*))
(sqlite:execute-single db-connection
"SELECT parent_id
FROM attribute
WHERE attribute_id = ?" attribute-id))
;; (attribute-id-to-name 359608984821) => ":inventory-condition-rating"
(defun attribute-id-to-name (attribute-id
&key (db-connection *mystuff2-sqlite-db-connection*))
;; (declare (integer category-id))
(sqlite:execute-single db-connection
"SELECT name
FROM attribute
WHERE attribute_id = ?" attribute-id))
;; (item-id-category-id 353345209773)
(defun item-id-category-id (item-id
&key (db-connection *mystuff2-sqlite-db-connection*))
(sqlite:execute-single db-connection
"SELECT category_id
FROM item
WHERE item_id = ?" item-id))
;; (item-id-to-category-name "355763820635")
;; (item-id-to-category-name "355763820635c")
(defun item-id-to-category-name (item-id &key (db-connection *mystuff2-sqlite-db-connection*))
(let ((maybe-cat-id (item-id-category-id item-id :db-connection db-connection)))
(when maybe-cat-id
(category-id-to-name maybe-cat-id :db-connection db-connection))))
;; (category-set-name-to-category-set-id "fashion-inventory") => 355763818796
(defun category-set-name-to-category-set-id (category-set-name
&key (db-connection *mystuff2-sqlite-db-connection*))
(sqlite:execute-single db-connection
"SELECT category_set.set_id
FROM category_set
WHERE category_set.name = ?" category-set-name))
;; (category-set-id-to-category-set-name "show-inventory") => 355763818796
;; (category-set-id-to-category-set-name (category-set-name-to-category-set-id "fashion-inventory")) => "fashion-inventory"
(defun category-set-id-to-category-set-name (category-set-id
&key (db-connection *mystuff2-sqlite-db-connection*))
(sqlite:execute-single db-connection
"SELECT category_set.name
FROM category_set
WHERE category_set.set_id = ?" category-set-id))
;; (all-category-ids-with-category-set-id 355763818796)
;; (all-category-ids-with-category-set-id (category-set-name-to-category-set-id "fashion-inventory"))
(defun all-category-ids-with-category-set-id (category-set-id
&key (db-connection *mystuff2-sqlite-db-connection*))
(loop
with statement = (sqlite:prepare-statement db-connection
"SELECT category_id
FROM category_set_cat
WHERE category_set_cat.set_id = ?;")
initially (sqlite:bind-parameter statement 1 category-set-id)
while (sqlite:step-statement statement)
collect (sqlite:statement-column-value statement 0)
finally (sqlite:finalize-statement statement)))
;; (all-category-ids-with-category-set-name "fashion-inventory")
;; => (355763818667 355763818823 355763820224 355763820231 355763820239
;; 355763820247 355763820613 355798450706)
(defun all-category-ids-with-category-set-name (category-set-name
&key (db-connection *mystuff2-sqlite-db-connection*))
(let ((maybe-cat-set-id (category-set-name-to-category-set-id category-set-name :db-connection db-connection)))
(when maybe-cat-set-id
(all-category-ids-with-category-set-id maybe-cat-set-id :db-connection db-connection))))
;; (all-category-names-with-category-set-name "fashion-inventory")
(defun all-category-names-with-category-set-name (category-set-name
&key (db-connection *mystuff2-sqlite-db-connection*))
(let* ((maybe-cat-set-id (category-set-name-to-category-set-id category-set-name :db-connection db-connection))
(maybe-category-ids
(when maybe-cat-set-id
(all-category-ids-with-category-set-id maybe-cat-set-id :db-connection db-connection))))
(when maybe-category-ids
(loop
for cat-id in maybe-category-ids
when (category-id-to-name cat-id :db-connection db-connection)
collect it))))
;; (category-set-id-for-category-id "355763818823") => 355763818796
(defun category-set-id-for-category-id (category-id
&key (db-connection *mystuff2-sqlite-db-connection*))
(sqlite:execute-single db-connection
"SELECT category_set_cat.set_id
FROM category_set_cat
WHERE category_set_cat.category_id = ?" category-id))
;; (category-set-name-for-category-id "355763818823") => "fashion-inventory"
(defun category-set-name-for-category-id (category-id
&key (db-connection *mystuff2-sqlite-db-connection*))
(let ((maybe-cat-set-id (category-set-id-for-category-id category-id :db-connection db-connection)))
(when maybe-cat-set-id
(sqlite:execute-single db-connection
"SELECT category_set.name
FROM category_set
WHERE category_set.set_id = ?" maybe-cat-set-id))))
;; (all-item-ids-with-category-id 355763820239 )
;; (length (all-item-ids-with-category-id 355763820224))
;; (length (all-item-ids-with-category-id 355763820239))
(defun all-item-ids-with-category-id (category-id
&key (db-connection *mystuff2-sqlite-db-connection*))
(loop
with statement = (sqlite:prepare-statement db-connection
"SELECT item.item_id
FROM item
WHERE item.category_id = ?")
initially (sqlite:bind-parameter statement 1 category-id)
while (sqlite:step-statement statement)
collect (sqlite:statement-column-value statement 0)
finally (sqlite:finalize-statement statement)))
;; (category-set-name-to-category-set-id "fashion-inventory") => 355763818796
;; (length (all-item-ids-with-category-set-id 355763818796)) => 1846
(defun all-item-ids-with-category-set-id (category-set-id
&key (db-connection *mystuff2-sqlite-db-connection*))
(let ((categories (all-category-ids-with-category-set-id category-set-id :db-connection db-connection)))
(when categories
(loop
for category in categories
nconcing (all-item-ids-with-category-id category :db-connection db-connection)))))
;; (attribute-id-with-attribute-name-and-category-id ":inventory-number" 355763820224)
(defun attribute-id-with-attribute-name-and-category-id (attribute-name category-id
&key (db-connection *mystuff2-sqlite-db-connection*))
(sqlite:execute-single db-connection
"SELECT attribute.attribute_id
FROM attribute
WHERE parent_id = ?
AND name = ?;" category-id attribute-name))
;; (all-attribute-ids-with-attribute-name-and-category-set-name ":inventory-number" "fashion-inventory")
(defun all-attribute-ids-with-attribute-name-and-category-set-name (attribute-name category-set-name
&key (db-connection *mystuff2-sqlite-db-connection*))
(let* ((category-set-id (category-set-name-to-category-set-id category-set-name :db-connection db-connection))
(categories (and category-set-id (all-category-ids-with-category-set-id category-set-id :db-connection db-connection))))
(when categories
(loop
for category in categories
collect (attribute-id-with-attribute-name-and-category-id attribute-name category :db-connection db-connection)))))
;; (all-attribute-ids-with-attribute-name-and-category-set-id ":inventory-number" 355763818796)
;; (length (all-attribute-ids-with-attribute-name-and-category-set-id ":inventory-number" 355763818796)
(defun all-attribute-ids-with-attribute-name-and-category-set-id (attribute-name category-set-id
&key (db-connection *mystuff2-sqlite-db-connection*))
(let ((categories (all-category-ids-with-category-set-id category-set-id :db-connection db-connection)))
(when categories
(loop
for category in categories
collect (attribute-id-with-attribute-name-and-category-id attribute-name category :db-connection db-connection)))))
;; (all-item-ids-and-text-values-with-attribute-id-as-conses 355763820225)
;; (all-item-ids-and-text-values-with-attribute-id-as-conses 355763820225 :item-ids-as-string t)
(defun all-item-ids-and-text-values-with-attribute-id-as-conses (attribute-id
&key (db-connection *mystuff2-sqlite-db-connection*)
(item-ids-as-string nil))
(loop
with statement = (sqlite:prepare-statement db-connection
"SELECT item_value.item_id, item_value.text_value
FROM item_value
WHERE item_value.attribute_id = ?;")
initially (sqlite:bind-parameter statement 1 attribute-id)
while (sqlite:step-statement statement)
collect (cons (or (and item-ids-as-string (princ-to-string (sqlite:statement-column-value statement 0)))
(sqlite:statement-column-value statement 0))
(sqlite:statement-column-value statement 1))
finally (sqlite:finalize-statement statement)))
;; (all-item-ids-and-text-values-with-attribute-id-to-hash-table 355763820225 (make-hash-table :test 'equal) :item-ids-as-string t)
;; Following errors successfully:
;; (all-item-ids-and-text-values-with-attribute-id-to-hash-table 355763820225 (make-hash-table) :item-ids-as-string t)
(defun all-item-ids-and-text-values-with-attribute-id-to-hash-table (attribute-id hash-table &key (db-connection *mystuff2-sqlite-db-connection*)
(item-ids-as-string nil)
(%checking-hash-table-test% t))
(when (and item-ids-as-string %checking-hash-table-test%)
(unless (eql (hash-table-test hash-table) 'equal)
(error ":FUNCTION `all-item-ids-and-text-values-with-attribute-id-to-hash-table'~% ~
-- with keyword ITEM-IDS-AS-STRING non-nil HASH-TABLE with `cl:hash-table-test' not `equal'~% ~
hash-table: ~S~%" hash-table)))
(loop
with statement = (sqlite:prepare-statement db-connection
"SELECT item_value.item_id, item_value.text_value
FROM item_value
WHERE item_value.attribute_id = ?;")
initially (sqlite:bind-parameter statement 1 attribute-id)
while (sqlite:step-statement statement)
do (setf (gethash (or (and item-ids-as-string (princ-to-string (sqlite:statement-column-value statement 0)))
(sqlite:statement-column-value statement 0))
hash-table)
(sqlite:statement-column-value statement 1))
finally (sqlite:finalize-statement statement))
hash-table)
;; (length *tt-cons/hash*)
;; (defparameter *tt-cons/hash* nil)
;; (setf *tt-cons/hash* nil)
;;
;; (all-item-ids-and-text-values-with-attribute-name-and-category-set-name-as-conses ":inventory-number"
;; "fashion-inventory"
;; :item-ids-as-string t)
(defun all-item-ids-and-text-values-with-attribute-name-and-category-set-name-as-conses (attribute-name category-set-name
&key (db-connection *mystuff2-sqlite-db-connection*)
(item-ids-as-string nil))
(let ((attribute-ids (all-attribute-ids-with-attribute-name-and-category-set-name attribute-name
category-set-name
:db-connection db-connection)))
(when attribute-ids
(loop
for attribute-id in attribute-ids
nconcing (all-item-ids-and-text-values-with-attribute-id-as-conses attribute-id
:db-connection db-connection
:item-ids-as-string item-ids-as-string)))))
;; (defparameter *item-ids-hash-table* nil)
;; (setf *item-ids-hash-table*
;; (all-item-ids-and-text-values-with-attribute-name-and-category-set-name-to-hash-table ":inventory-number"
;; "fashion-inventory"
;; (make-hash-table :test 'equal)
;; :item-ids-as-string t))
;; Following errors successfully:
;; (all-item-ids-and-text-values-with-attribute-name-and-category-set-name-to-hash-table ":inventory-number"
;; "fashion-inventory"
;; (make-hash-table)
;; :item-ids-as-string t)
(defun all-item-ids-and-text-values-with-attribute-name-and-category-set-name-to-hash-table (attribute-name category-set-name hash-table
&key (db-connection *mystuff2-sqlite-db-connection*)
(item-ids-as-string nil))
"Return a hash-table with sqlite item-ids as hash-key and the value of the
attribute-name associated with the item-id as hash-value.
Returned hash-table is intended for use with `zip-entry-image-paths-for-extract'.
The hash-key/hash-value pairs of returned hash-table have the form:
<SQLITE-ITEM-ID> <ATTRIBUTE-VALUE>
\"355763819863\" \"017064-BTTN\"
:EXAMPLE
\(defparameter *item-ids-hash-table* nil\)
\(setf *item-ids-hash-table*
\(all-item-ids-and-text-values-with-attribute-name-and-category-set-name-to-hash-table \":inventory-number\"
\"fashion-inventory\"
\(make-hash-table :test 'equal\)
:item-ids-as-string t\)\) "
(when item-ids-as-string
(unless (eql (hash-table-test hash-table) 'equal)
(error ":FUNCTION `all-item-ids-and-text-values-with-attribute-name-and-category-set-name-to-hash-table'~% ~
-- with keyword ITEM-IDS-AS-STRING non-nil HASH-TABLE with `cl:hash-table-test' not `equal'~% ~
hash-table: ~S~%" hash-table)))
(let ((attribute-ids
(all-attribute-ids-with-attribute-name-and-category-set-name attribute-name
category-set-name
:db-connection db-connection)))
(when attribute-ids
(loop
for attribute-id in attribute-ids
do (all-item-ids-and-text-values-with-attribute-id-to-hash-table
attribute-id
hash-table
:db-connection db-connection
:item-ids-as-string item-ids-as-string
:%checking-hash-table-test% nil)))
hash-table))
;; (item-id-inventory-number 356161032450) => "018145-WEAR"
;; (item-id-inventory-number 356161032450 ":inventory-number")
;;
;; Following fails succesfully:
;; (item-id-inventory-number 356161032450 :inventory-id-key ":bar")
;; Following errors succesfully:
;; (item-id-inventory-number 1999999999999)
;;
;; :NOTE This is likely horribly inneficient.
(defun item-id-inventory-number (item-id &key (inventory-id-key ":inventory-number")
(db-connection *mystuff2-sqlite-db-connection*))
(declare (type (unsigned-byte 40) item-id)
(string inventory-id-key))
(sqlite:execute-single db-connection
;; "SELECT item_value.text_value, item_value.item_id, attribute.attribute_id, attribute.name, attribute.parent_id,
;; "SELECT item_value.text_value, item_value.item_id, attribute.attribute_id, attribute.name
"SELECT item_value.text_value
FROM item_value, attribute
WHERE item_value.item_id = ?
AND item_value.attribute_id = attribute.attribute_id
AND attribute.name = ?;" ; AND attribute.name = \":inventory-number\";"
item-id inventory-id-key))
;;; ==============================
;;; EOF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment