128 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Factor
		
	
	
		
		
			
		
	
	
			128 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Factor
		
	
	
|  | ! Copyright (C) 2005 Chris Double. | ||
|  | ! See http://factorcode.org/license.txt for BSD license. | ||
|  | !
 | ||
|  | ! An interface to the sqlite database. Tested against sqlite v3.0.8. | ||
|  | !
 | ||
|  | ! Not all functions have been wrapped yet. Only those directly involving | ||
|  | ! executing SQL calls and obtaining results. | ||
|  | !
 | ||
|  | IN: sqlite | ||
|  | USING: alien compiler kernel namespaces sequences strings sqlite.lib | ||
|  |     alien.c-types continuations ;
 | ||
|  | 
 | ||
|  | TUPLE: sqlite-error n message ;
 | ||
|  | SYMBOL: db | ||
|  | 
 | ||
|  | ! High level sqlite routines | ||
|  | : sqlite-check-result ( result -- )
 | ||
|  |   #! Check the result from a sqlite call is ok. If it is | ||
|  |   #! return, otherwise throw an error. | ||
|  |   dup SQLITE_OK = [ | ||
|  |     drop  | ||
|  |   ] [ | ||
|  |     dup sqlite-error-messages nth
 | ||
|  |     \ sqlite-error construct-boa throw
 | ||
|  |   ] if ;
 | ||
|  | 
 | ||
|  | : sqlite-open ( filename -- db )
 | ||
|  |   #! Open the database referenced by the filename and return | ||
|  |   #! a handle to that database. An error is thrown if the database | ||
|  |   #! failed to open. | ||
|  |   "void*" <c-object> [ sqlite3_open sqlite-check-result ] keep *void* ;
 | ||
|  | 
 | ||
|  | : sqlite-close ( db -- )
 | ||
|  |   #! Close the given database | ||
|  |   sqlite3_close sqlite-check-result ;
 | ||
|  | 
 | ||
|  | : sqlite-last-insert-rowid ( db -- rowid )
 | ||
|  |   #! Return the rowid of the last insert | ||
|  |   sqlite3_last_insert_rowid ;
 | ||
|  | 
 | ||
|  | : sqlite-prepare ( db sql -- statement )
 | ||
|  |   #! Prepare a SQL statement. Returns the statement which | ||
|  |   #! can have values bound to parameters or simply executed. | ||
|  |   #! TODO: Support multiple statements in the SQL string. | ||
|  |   dup length "void*" <c-object> "void*" <c-object> | ||
|  |   [ sqlite3_prepare sqlite-check-result ] 2keep
 | ||
|  |   drop *void* ;
 | ||
|  | 
 | ||
|  | : sqlite-bind-text ( statement index text -- )
 | ||
|  |   #! Bind the text to the parameterized value in the statement.   | ||
|  |   dup length SQLITE_TRANSIENT sqlite3_bind_text sqlite-check-result ;
 | ||
|  | 
 | ||
|  | : sqlite-bind-parameter-index ( statement name -- index )
 | ||
|  |   sqlite3_bind_parameter_index ;
 | ||
|  | 
 | ||
|  | : sqlite-bind-text-by-name ( statement name text -- )
 | ||
|  |   >r dupd sqlite-bind-parameter-index r> sqlite-bind-text ;
 | ||
|  | 
 | ||
|  | : sqlite-finalize ( statement -- )
 | ||
|  |   #! Clean up all resources related to a statement. Once called | ||
|  |   #! the statement cannot be used. All statements must be finalized | ||
|  |   #! before closing the database. | ||
|  |   sqlite3_finalize sqlite-check-result ;
 | ||
|  | 
 | ||
|  | : sqlite-reset ( statement -- )
 | ||
|  |   #! Reset a statement so it can be called again, possibly with | ||
|  |   #! different parameters. | ||
|  |   sqlite3_reset sqlite-check-result ;
 | ||
|  | 
 | ||
|  | : column-count ( statement -- int )
 | ||
|  |   #! Given a prepared statement, return the number of | ||
|  |   #! columns in each row of the result set of that statement. | ||
|  |   sqlite3_column_count ;
 | ||
|  | 
 | ||
|  | : column-text ( statement index -- string )
 | ||
|  |   #! Return the value of the given column, indexed | ||
|  |   #! from zero, as a string. | ||
|  |   sqlite3_column_text ;
 | ||
|  | 
 | ||
|  | : step-complete? ( step-result -- bool )
 | ||
|  |   #! Return true if the result of a sqlite3_step is | ||
|  |   #! such that the iteration has completed (ie. it is | ||
|  |   #! SQLITE_DONE). Throw an error if an error occurs.  | ||
|  |   dup SQLITE_ROW =  [ | ||
|  |     drop f
 | ||
|  |   ] [ | ||
|  |     dup SQLITE_DONE = [ | ||
|  |       drop t  | ||
|  |     ] [ | ||
|  |       sqlite-check-result t
 | ||
|  |     ] if
 | ||
|  |   ] if ;
 | ||
|  | 
 | ||
|  | : sqlite-each ( statement quot -- )     | ||
|  |   #! Execute the SQL statement, and call the quotation for | ||
|  |   #! each row returned from executing the statement with the | ||
|  |   #! statement on the top of the stack. | ||
|  |   over sqlite3_step step-complete? [  | ||
|  |     2drop
 | ||
|  |   ] [ | ||
|  |     [ call ] 2keep sqlite-each | ||
|  |   ] if ; inline
 | ||
|  | 
 | ||
|  | ! For comparison, here is the linrec implementation of sqlite-each | ||
|  | ! [ drop sqlite3_step step-complete? ] | ||
|  | ! [ 2drop ] | ||
|  | ! [ 2dup 2slip ] | ||
|  | ! [ ] linrec ;  | ||
|  | 
 | ||
|  | DEFER: (sqlite-map) | ||
|  | 
 | ||
|  | : (sqlite-map) ( statement quot seq -- )     | ||
|  |   pick sqlite3_step step-complete? [  | ||
|  |     2nip
 | ||
|  |   ] [ | ||
|  |     >r 2dup call r> swap add (sqlite-map) | ||
|  |   ] if ;  | ||
|  | 
 | ||
|  | : sqlite-map ( statement quot -- seq )
 | ||
|  |   { } (sqlite-map) ;
 | ||
|  | 
 | ||
|  | : with-sqlite ( path quot -- )
 | ||
|  |     [ | ||
|  |         >r sqlite-open db set r> | ||
|  |         [ db get sqlite-close ] [ ] cleanup
 | ||
|  |     ] with-scope ;
 | ||
|  | 
 |