2 ;; @description SQLite3 database interface routines
3 ;; @version 1.6 - comments redone for automatic documentation
4 ;; @version 1.7 - a fix getting types when null values are present (thanks Dmitry)
5 ;; @version 1.8 - a fix to make 64-Bit integers work (thanks Dmitry)
6 ;; @version 1.9 - new library detection routine
7 ;; @author Lutz Mueller, 2004-7
9 ;; <h2>Module for SQLite3 database bindings</h2>
10 ;; To use this module include the following 'load' statement at the
11 ;; beginning of the program file:
13 ;; (load "/usr/share/newlisp/modules/sqlite3.lsp")
16 ;; SQLite version 3.0 introduced a new database format and is incompatible
17 ;; whith the previous 2.1 to 2.8 format. Old SQLite 2.x based databases can
18 ;; be converted using the old and new sqlite client application:
20 ;; sqlite OLD.DB .dump | sqlite3 NEW.DB
22 ;; While in sqlite 2.8 all returned fields where of string type, SQLite3
23 ;; returns, text, integer or float. Blobs are returned as text and NULLs
24 ;; are returned as nil.
26 ;; See also the documentation at @link http://sqlite.org sqlite.org
28 ;; <h2>Requirements:</h2>
29 ;; One of the libraries sqlite3.dll for Win32 or libsqlite3.so for UNIX like
30 ;; operating systems is required from http://www.sqlite.org.
32 ;; SQLite is an <in-process> database. The library contains the whole database
33 ;; system. An extra database server is not required. SQLite also has limited
34 ;; mutiuser capabilities for accessing a common database from several programs
35 ;; at the same time. See the documentaion at @link http://sqlite.org sqlite.org
38 ;; The following is a short example how to use SQLite3:
41 ;; (sql3:open "MYDB") ; opens/creates a database returns a handle (ignore)
42 ;; ; or 'nil' on failure
44 ;; (sql3:sql "select * from mytable;") ; make a SQL query, return result
46 ;; (sql3:error) ; return error text
48 ;; (sql3:close) ; close the database
50 ;; Function calls returning 'nil' signal that an error has occurred. The
51 ;; function 'sql3:error' can then be used to get details about the error
54 ;; At the bottom of the source file 'sqlite3.lsp' a test routine called
55 ;; 'test-sqlite3' can be found to test for correct installation of SQLite.
59 ; set library to path-name of the library on your platform OS
62 "/usr/local/lib/libsqlite3.so" ; Linyx and BSDs
63 "/usr/lib/libsqlite3.0.dylib" ; Mac OSX Darwin
64 "/usr/local/lib/libsqlite3.so" ; Solaris
65 (string (env "PROGRAMFILES") "/sqlite3/sqlite3.dll") ; Win32/MinGW
69 (set 'library
(files (or
70 (find true
(map file? files
))
71 (begin (println "cannot find sqlite3 library") (exit)))))
73 (import library
"sqlite3_open" "cdecl")
74 (import library
"sqlite3_close" "cdecl")
75 (import library
"sqlite3_prepare" "cdecl")
76 (import library
"sqlite3_step" "cdecl")
77 (import library
"sqlite3_column_count" "cdecl")
78 (import library
"sqlite3_column_name" "cdecl")
79 (import library
"sqlite3_column_type" "cdecl")
80 (import library
"sqlite3_column_int" "cdecl")
81 (import library
"sqlite3_column_double" "cdecl")
82 (import library
"sqlite3_column_text" "cdecl")
83 (import library
"sqlite3_column_blob" "cdecl")
84 (import library
"sqlite3_column_bytes" "cdecl")
85 (import library
"sqlite3_finalize" "cdecl")
86 (import library
"sqlite3_get_table" "cdecl")
87 (import library
"sqlite3_last_insert_rowid" "cdecl")
88 (import library
"sqlite3_changes" "cdecl")
89 (import library
"sqlite3_busy_timeout" "cdecl")
90 (import library
"sqlite3_errmsg" "cdecl")
93 ; gloablly used vars and constants
95 (define db nil
) ; database handle
96 (define dbp
"\000\000\000\000") ; ptr to database handle
97 (define error-message nil
) ; error message
98 (define col-names
'()) ; list of column headers
99 (define col-types
'()) ; list of column types
100 (define pstm
"\000\000\000\000") ; ptr to compiled sql
102 (constant 'SQLITE_OK
0)
103 (constant 'SQLITE_ROW
100)
104 (constant 'SQLITE_DONE
101)
106 (constant 'SQLITE_TYPES
'(
114 ;; @syntax (sql3:open <str-db-name>)
115 ;; @param <str-db-name> The name of the database.
116 ;; @return A database handle (discard), or 'nil' on failure.
118 ;; Opens or creates a database. If the database does exist it gets opened,
119 ;; else a new database with the name given is created.
120 ;; If trying to open a database that already has been opened 'nil' is returned
121 ;; and an error text can be retrieved using 'sql3:error'.
123 (define (sql3:open db-name
)
124 ; only open if not alrady done
127 (set 'result
(sqlite3_open db-name dbp
))
128 (if (!= result SQLITE_OK
)
130 (set 'db
(get-int dbp
))))
132 (set 'error-message
"A database is already open")
136 ; close the currently open database ;;;
139 (define (sql3:close
) ;; overwrite the close in MAIN
145 ;; @syntax (sql3:sql <str-sql>)
146 ;; @param <str-sql> The SQL statement.
148 ;; Executes the SQL statement in <str-sql>. For 'select' statements an array
149 ;; of the result set is returned or '()' for the empty set. For other statements
150 ;; 'true' is returned for a successful outcome. On failure 'nil' is returened
151 ;; and 'sql3:error' can be used to retrieve the error text.
153 (define (sql sql-str
)
154 (set 'result nil
'done nil
'error-message nil
)
156 (set 'col-names
'());
157 (set 'col-types
'());
159 ; set up parameters for sqlite3_prepare() call
160 (set 'ppstm
"\000\000\000\000") ; pointer to statement ptr
161 (set 'pptail
"\000\000\000\000") ; pointer to statement tail
163 ; compile the sql statment
164 (if db
(set 'result
(sqlite3_prepare db sql-str -
1 ppstm pptail
)))
166 ; set up parameters for sqlite3_step() call
167 (set 'pstm
(get-int ppstm
))
169 ; execute the compiled statement
170 (if (= result SQLITE_OK
)
172 ;; execute statement until done/101 or
173 (set 'result
(sqlite3_step pstm
))
174 (set 'num-cols
(sqlite3_column_count pstm
))
175 (if (empty? col-names
) (set 'col-names
(get-names pstm num-cols
)))
176 (set 'col-types
(get-types pstm num-cols
))
177 (if (= result SQLITE_ROW
)
178 (push (get-values pstm num-cols
) sqarray -
1)
179 (set 'done true
) ;; received done/101 or error
183 ; if done/101 finalize
184 (if (= result SQLITE_DONE
)
186 (set 'result
(sqlite3_finalize pstm
))
187 ; for 'select' statements return the array else 'true'
188 (if (> num-cols
0) sqarray true
))
189 (if (= result
0) true
(set-error))))
192 (define (get-values pstm cols
)
195 (set 'i
(int idx
)) ; all loop vars are float
196 (case (nth idx col-types idx
)
198 ; (push (sqlite3_column_int pstm i) row -1))
199 ; fixed for 64-bit, thanks Dmitry
201 (set 'pstr
(sqlite3_column_text pstm i
))
204 (push (int (get-string pstr
)) row -
1)))
206 (set 'pstr
(sqlite3_column_text pstm i
))
209 (push (float (get-string pstr
)) row -
1)))
211 (set 'pstr
(sqlite3_column_text pstm i
))
214 (push (get-string pstr
) row -
1)))
218 (set 'pstr
(sqlite3_column_blob pstm i
))
219 (set 'len
(sqlite3_column_bytes pstm i
))
220 (set 'buff
(dup "\000" len
))
224 (cpymem pstr buff len
)
225 (push buff row -
1))))
230 (define (get-names pstm cols
)
233 (set 'i
(int idx
)) ; all loop vars are float
234 (set 'ps
(sqlite3_column_name pstm i
))
235 (if (= ps
0) ;; check for null pointer to result
237 (push (get-string ps
) row -
1)))
240 (define (get-types pstm cols
)
243 (set 'i
(int idx
)) ; all loop vars are float
244 (push (nth (sqlite3_column_type pstm i
) SQLITE_TYPES
) row -
1))
248 ;; @syntax (sql3:rowid)
249 ;; @return The last row id from last 'insert'.
252 (if db
(sqlite3_last_insert_rowid db
)))
254 ;; @syntax (sql3:tables)
255 ;; @return A list of tables in the database.
259 (set 'lst
(sql "select tbl_name from sqlite_master")) ))
260 (if lst
(set 'lst
(first (transpose lst
)))))
263 ;; @syntax (sql3:columns <str-tabel-name>)
264 ;; @return A list of column names for a table.
266 (define (columns aTable
)
267 (if (list?
(sql (append "select * from " aTable
" where 0;")))
271 ;; @syntax (sql3:changes)
272 ;; @return The Number of rows changed/affected by the last SQL statement.
275 (if db
(sqlite3_changes db
)))
279 ;; @syntax (sql3:timeout <num-milli-seconds>)
280 ;; @return 'true' on success or 'nil' on failure.
282 ;; Sets busy timeout in milliseconds.
285 (if db
(zero?
(sqlite3_busy_timeout db
(int ms
)))))
289 ;; @syntax (sql3:error)
290 ;; @return The error text of the last error occured in 'sql3:sql'.
292 (define (error) error-message
)
295 (set 'result
(sqlite3_errmsg db
))
297 (set 'error-message nil
)
298 (set 'error-message
(get-string result
))
306 ; -------------------------------------------------------------------------
308 ; test the database routines
310 ; if there is an old "TEST" db from an earlier sqlite 2.8 delete it first
312 (define (test-sqlite3)
313 (if (sql3:open
"TEST")
314 (println "database opened/created, ... Ok")
315 (println "problem opening/creating database"))
317 (if (sql3:sql
"create table fruits (name CHAR(20), qty INT(3), price FLOAT(10), blobtext BLOB);")
318 (println "created table fruits, ... Ok")
319 (println "problem creating table fruits"))
321 (if (sql3:sql
"insert into fruits values ('apples', 11, 1.234, X'41424300010101');")
322 (println "inserted, last row id: " (sql3:rowid
) ", ... Ok")
323 (println "problem inserting row"))
325 (if (sql3:sql
"insert into fruits values ('oranges', 22, 2.345, X'42434400020202');")
326 (println "inserted, last row id: " (sql3:rowid
) ", ... Ok")
327 (println "problem inserting row"))
329 (if (sql3:sql
"insert into fruits values ('bananas', 33, 3.456, X'44454600030303');")
330 (println "inserted, last row id: " (sql3:rowid
) ", ... Ok")
331 (println "problem inserting row"))
334 "insert into fruits values ('grapes', 123456789012345678, 7.89, X'47484900040404');")
335 (println "inserted, last row id: " (sql3:rowid
) ", ... Ok")
336 (println "problem inserting row"))
338 (set 'sqarray
(sql3:sql
"select * from fruits;"))
342 (println "selected rows: ")
343 (map println sqarray
)
344 (println "column names: ")
345 (map println sql3
:col-names
)
348 (println "problem with select"))
350 (if (sql3:sql
"delete from fruits where 1;")
351 (println "deleted, rows affected: " (sql3:changes
) ", ... Ok")
352 (println "problem deleting rows"))
354 (if (list?
(sql3:tables
) )
355 (println "tables: " (sql3:tables
) ", ... Ok")
356 (println "problem in sql3:tables"))
358 (if (list?
(sql3:columns
"fruits") )
359 (println "columns: " (sql3:columns
"fruits") ", ... Ok")
360 (println "problem in sql3:columns"))
363 (if (sql3:sql
"drop table fruits;")
364 (println "table fruits dropped, ... Ok")
365 (println "problem dropping table fruits"))