+
+;;; Object listing
+
+(defmethod database-list-tables ((database sqlite-database) &key owner)
+ (declare (ignore owner))
+ ;; Query is copied from .table command of sqlite comamnd line utility.
+ (remove-if #'(lambda (s)
+ (and (>= (length s) 11)
+ (string= (subseq s 0 11) "_clsql_seq_")))
+ (mapcar #'car (database-query
+ "SELECT name FROM sqlite_master WHERE type='table' UNION ALL SELECT name FROM sqlite_temp_master WHERE type='table' ORDER BY name"
+ database '()))))
+
+(defmethod database-list-views ((database sqlite-database)
+ &key (owner nil))
+ (declare (ignore owner))
+ (mapcar #'car (database-query
+ "SELECT name FROM sqlite_master WHERE type='view' UNION ALL SELECT name FROM sqlite_temp_master WHERE type='view' ORDER BY name"
+ database nil)))
+
+(defmethod database-list-indexes ((database sqlite-database)
+ &key (owner nil))
+ (declare (ignore owner))
+ (mapcar #'car (database-query
+ "SELECT name FROM sqlite_master WHERE type='index' UNION ALL SELECT name FROM sqlite_temp_master WHERE type='index' ORDER BY name"
+ database nil)))
+
+(defmethod database-list-table-indexes (table (database sqlite-database)
+ &key (owner nil))
+ (declare (ignore owner))
+ (mapcar #'car
+ (database-query
+ (format
+ nil
+ "SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='~A' UNION ALL SELECT name FROM sqlite_temp_master WHERE type='index' AND tbl_name='~A' ORDER BY name"
+ table table)
+ database nil)))
+
+(declaim (inline sqlite-table-info))
+(defun sqlite-table-info (table database)
+ (database-query (format nil "PRAGMA table_info('~A')" table)
+ database '()))
+
+(defmethod database-list-attributes (table (database sqlite-database)
+ &key (owner nil))
+ (declare (ignore owner))
+ (mapcar #'(lambda (table-info) (second table-info))
+ (sqlite-table-info table database)))
+
+(defmethod database-attribute-type (attribute table
+ (database sqlite-database)
+ &key (owner nil))
+ (declare (ignore owner))
+ (loop for field-info in (sqlite-table-info table database)
+ when (string= attribute (second field-info))
+ return (third field-info)))
+
+(defun %sequence-name-to-table-name (sequence-name)
+ (concatenate 'string "_clsql_seq_" (sql-escape sequence-name)))
+
+(defun %table-name-to-sequence-name (table-name)
+ (and (>= (length table-name) 11)
+ (string= (subseq table-name 0 11) "_clsql_seq_")
+ (subseq table-name 11)))
+
+
+(defmethod database-create-sequence (sequence-name
+ (database sqlite-database))
+ (let ((table-name (%sequence-name-to-table-name sequence-name)))
+ (database-execute-command
+ (concatenate 'string "CREATE TABLE " table-name
+ " (last_value integer PRIMARY KEY, increment_by integer, min_value integer, is_called char(1))")
+ database)
+ (database-execute-command
+ (concatenate 'string "INSERT INTO " table-name
+ " VALUES (1,1,1,'f')")
+ database)))
+
+(defmethod database-drop-sequence (sequence-name
+ (database sqlite-database))
+ (database-execute-command
+ (concatenate 'string "DROP TABLE " (%sequence-name-to-table-name sequence-name))
+ database))
+
+(defmethod database-list-sequences ((database sqlite-database)
+ &key (owner nil))
+ (declare (ignore owner))
+ (mapcan #'(lambda (s)
+ (let ((sn (%table-name-to-sequence-name (car s))))
+ (and sn (list sn))))
+ (database-query
+ "SELECT name FROM sqlite_master WHERE type='table' UNION ALL SELECT name FROM sqlite_temp_master WHERE type='table' ORDER BY name"
+ database '())))
+
+(defmethod database-sequence-next (sequence-name (database sqlite-database))
+ (without-interrupts
+ (let* ((table-name (%sequence-name-to-table-name sequence-name))
+ (tuple
+ (car (database-query
+ (concatenate 'string "SELECT last_value,is_called FROM "
+ table-name)
+ database
+ :auto))))
+ (cond
+ ((char-equal (schar (second tuple) 0) #\f)
+ (database-execute-command
+ (format nil "UPDATE ~A SET is_called='t'" table-name)
+ database)
+ (parse-integer (car tuple)))
+ (t
+ (let ((new-pos (1+ (parse-integer (car tuple)))))
+ (database-execute-command
+ (format nil "UPDATE ~A SET last_value=~D" table-name new-pos)
+ database)
+ new-pos))))))
+
+(defmethod database-sequence-last (sequence-name (database sqlite-database))
+ (without-interrupts
+ (parse-integer
+ (caar (database-query
+ (concatenate 'string "SELECT last_value FROM "
+ (%sequence-name-to-table-name sequence-name))
+ database
+ :auto)))))
+
+(defmethod database-set-sequence-position (sequence-name
+ (position integer)
+ (database sqlite-database))
+ (database-execute-command
+ (format nil "UPDATE ~A SET last_value=~A,is_called='t'"
+ (%sequence-name-to-table-name sequence-name)
+ position)
+ database)
+ position)
+
+(defmethod database-create (connection-spec (type (eql :sqlite)))
+ (declare (ignore connection-spec))
+ ;; databases are created automatically by SQLite
+ t)
+
+(defmethod database-destroy (connection-spec (type (eql :sqlite)))
+ (destructuring-bind (name) connection-spec
+ (if (probe-file name)
+ (delete-file name)
+ nil)))
+
+(defmethod database-probe (connection-spec (type (eql :sqlite)))
+ (destructuring-bind (name) connection-spec
+ ;; TODO: Add a test that this file is a real sqlite database
+ (or (string-equal ":memory:" name)
+ (and (probe-file name) t))))
+
+
+