first commit of db stuff
parent
2924f4bc45
commit
71358d3c4a
|
@ -0,0 +1,96 @@
|
|||
! Copyright (C) 2008 Doug Coleman.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: arrays assocs classes continuations kernel math
|
||||
namespaces sequences sequences.lib tuples words ;
|
||||
IN: db
|
||||
|
||||
TUPLE: db handle ;
|
||||
C: <db> db ( handle -- obj )
|
||||
|
||||
! HOOK: db-create db ( str -- )
|
||||
! HOOK: db-drop db ( str -- )
|
||||
GENERIC: db-open ( db -- )
|
||||
GENERIC: db-close ( db -- )
|
||||
|
||||
TUPLE: statement sql params handle bound? n max ;
|
||||
|
||||
TUPLE: simple-statement ;
|
||||
TUPLE: bound-statement ;
|
||||
TUPLE: prepared-statement ;
|
||||
TUPLE: prepared-bound-statement ;
|
||||
|
||||
HOOK: <simple-statement> db ( str -- statement )
|
||||
HOOK: <bound-statement> db ( str obj -- statement )
|
||||
HOOK: <prepared-statement> db ( str -- statement )
|
||||
HOOK: <prepared-bound-statement> db ( str obj -- statement )
|
||||
|
||||
! TUPLE: result sql params handle n max ;
|
||||
|
||||
GENERIC: #rows ( statement -- n )
|
||||
GENERIC: #columns ( statement -- n )
|
||||
GENERIC# row-column 1 ( statement n -- obj )
|
||||
GENERIC: advance-row ( statement -- ? )
|
||||
|
||||
GENERIC: prepare-statement ( statement -- )
|
||||
GENERIC: reset-statement ( statement -- )
|
||||
GENERIC: bind-statement* ( obj statement -- )
|
||||
GENERIC: rebind-statement ( obj statement -- )
|
||||
|
||||
: bind-statement ( obj statement -- )
|
||||
2dup dup statement-bound? [
|
||||
rebind-statement
|
||||
] [
|
||||
bind-statement*
|
||||
] if
|
||||
tuck set-statement-params
|
||||
t swap set-statement-bound? ;
|
||||
|
||||
: sql-row ( statement -- seq )
|
||||
dup #columns [ row-column ] with map ;
|
||||
|
||||
: query-each ( statement quot -- )
|
||||
over advance-row [
|
||||
2drop
|
||||
] [
|
||||
[ call ] 2keep query-each
|
||||
] if ; inline
|
||||
|
||||
: query-map ( statement quot -- seq )
|
||||
accumulator >r query-each r> { } like ; inline
|
||||
|
||||
: with-db ( db quot -- )
|
||||
[
|
||||
over db-open
|
||||
[ db swap with-variable ] curry with-disposal
|
||||
] with-scope ;
|
||||
|
||||
: do-statement ( statement -- )
|
||||
[ advance-row drop ] with-disposal ;
|
||||
|
||||
: do-query ( query -- rows )
|
||||
[ [ sql-row ] query-map ] with-disposal ;
|
||||
|
||||
: do-simple-query ( sql -- rows )
|
||||
<simple-statement> do-query ;
|
||||
|
||||
: do-bound-query ( sql obj -- rows )
|
||||
<bound-statement> do-query ;
|
||||
|
||||
: do-simple-command ( sql -- )
|
||||
<simple-statement> do-statement ;
|
||||
|
||||
: do-bound-command ( sql obj -- )
|
||||
<bound-statement> do-statement ;
|
||||
|
||||
SYMBOL: in-transaction
|
||||
HOOK: begin-transaction db ( -- )
|
||||
HOOK: commit-transaction db ( -- )
|
||||
HOOK: rollback-transaction db ( -- )
|
||||
|
||||
: in-transaction? ( -- ? ) in-transaction get ;
|
||||
|
||||
: with-transaction ( quot -- )
|
||||
t in-transaction [
|
||||
begin-transaction
|
||||
[ ] [ rollback-transaction ] cleanup commit-transaction
|
||||
] with-variable ;
|
|
@ -0,0 +1 @@
|
|||
Doug Coleman
|
|
@ -0,0 +1,360 @@
|
|||
! Copyright (C) 2007 Doug Coleman.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
|
||||
! adapted from libpq-fe.h version 7.4.7
|
||||
! tested on debian linux with postgresql 7.4.7
|
||||
! Updated to 8.1
|
||||
|
||||
USING: alien alien.syntax combinators system ;
|
||||
IN: db.postgresql.ffi
|
||||
|
||||
<<
|
||||
"postgresql" {
|
||||
{ [ win32? ] [ "libpq.dll" ] }
|
||||
{ [ macosx? ] [ "/opt/local/lib/postgresql81/libpq.dylib" ] }
|
||||
{ [ unix? ] [ "libpq.so" ] }
|
||||
} cond "cdecl" add-library
|
||||
>>
|
||||
|
||||
! ConnSatusType
|
||||
: CONNECTION_OK HEX: 0 ; inline
|
||||
: CONNECTION_BAD HEX: 1 ; inline
|
||||
: CONNECTION_STARTED HEX: 2 ; inline
|
||||
: CONNECTION_MADE HEX: 3 ; inline
|
||||
: CONNECTION_AWAITING_RESPONSE HEX: 4 ; inline
|
||||
: CONNECTION_AUTH_OK HEX: 5 ; inline
|
||||
: CONNECTION_SETENV HEX: 6 ; inline
|
||||
: CONNECTION_SSL_STARTUP HEX: 7 ; inline
|
||||
: CONNECTION_NEEDED HEX: 8 ; inline
|
||||
|
||||
! PostgresPollingStatusType
|
||||
: PGRES_POLLING_FAILED HEX: 0 ; inline
|
||||
: PGRES_POLLING_READING HEX: 1 ; inline
|
||||
: PGRES_POLLING_WRITING HEX: 2 ; inline
|
||||
: PGRES_POLLING_OK HEX: 3 ; inline
|
||||
: PGRES_POLLING_ACTIVE HEX: 4 ; inline
|
||||
|
||||
! ExecStatusType;
|
||||
: PGRES_EMPTY_QUERY HEX: 0 ; inline
|
||||
: PGRES_COMMAND_OK HEX: 1 ; inline
|
||||
: PGRES_TUPLES_OK HEX: 2 ; inline
|
||||
: PGRES_COPY_OUT HEX: 3 ; inline
|
||||
: PGRES_COPY_IN HEX: 4 ; inline
|
||||
: PGRES_BAD_RESPONSE HEX: 5 ; inline
|
||||
: PGRES_NONFATAL_ERROR HEX: 6 ; inline
|
||||
: PGRES_FATAL_ERROR HEX: 7 ; inline
|
||||
|
||||
! PGTransactionStatusType;
|
||||
: PQTRANS_IDLE HEX: 0 ; inline
|
||||
: PQTRANS_ACTIVE HEX: 1 ; inline
|
||||
: PQTRANS_INTRANS HEX: 2 ; inline
|
||||
: PQTRANS_INERROR HEX: 3 ; inline
|
||||
: PQTRANS_UNKNOWN HEX: 4 ; inline
|
||||
|
||||
! PGVerbosity;
|
||||
: PQERRORS_TERSE HEX: 0 ; inline
|
||||
: PQERRORS_DEFAULT HEX: 1 ; inline
|
||||
: PQERRORS_VERBOSE HEX: 2 ; inline
|
||||
|
||||
|
||||
TYPEDEF: int size_t
|
||||
TYPEDEF: int ConnStatusType
|
||||
TYPEDEF: int ExecStatusType
|
||||
TYPEDEF: int PostgresPollingStatusType
|
||||
TYPEDEF: int PGTransactionStatusType
|
||||
TYPEDEF: int PGVerbosity
|
||||
|
||||
TYPEDEF: void* PGconn*
|
||||
TYPEDEF: void* PGresult*
|
||||
TYPEDEF: void* PGcancel*
|
||||
TYPEDEF: uint Oid
|
||||
TYPEDEF: uint* Oid*
|
||||
TYPEDEF: char pqbool
|
||||
TYPEDEF: void* PQconninfoOption*
|
||||
TYPEDEF: void* PGnotify*
|
||||
TYPEDEF: void* PQArgBlock*
|
||||
TYPEDEF: void* PQprintOpt*
|
||||
TYPEDEF: void* FILE*
|
||||
TYPEDEF: void* SSL*
|
||||
|
||||
LIBRARY: postgresql
|
||||
|
||||
|
||||
! Exported functions of libpq
|
||||
! === in fe-connect.c ===
|
||||
|
||||
! make a new client connection to the backend
|
||||
! Asynchronous (non-blocking)
|
||||
FUNCTION: PGconn* PQconnectStart ( char* conninfo ) ;
|
||||
FUNCTION: PostgresPollingStatusType PQconnectPoll ( PGconn* conn ) ;
|
||||
|
||||
! Synchronous (blocking)
|
||||
FUNCTION: PGconn* PQconnectdb ( char* conninfo ) ;
|
||||
FUNCTION: PGconn* PQsetdbLogin ( char* pghost, char* pgport,
|
||||
char* pgoptions, char* pgtty,
|
||||
char* dbName,
|
||||
char* login, char* pwd ) ;
|
||||
|
||||
: PQsetdb ( M_PGHOST M_PGPORT M_PGOPT M_PGTTY M_DBNAME -- PGconn* )
|
||||
f f PQsetdbLogin ;
|
||||
|
||||
! close the current connection and free the PGconn data structure
|
||||
FUNCTION: void PQfinish ( PGconn* conn ) ;
|
||||
|
||||
! get info about connection options known to PQconnectdb
|
||||
FUNCTION: PQconninfoOption* PQconndefaults ( ) ;
|
||||
|
||||
! free the data structure returned by PQconndefaults()
|
||||
FUNCTION: void PQconninfoFree ( PQconninfoOption* connOptions ) ;
|
||||
|
||||
!
|
||||
! close the current connection and restablish a new one with the same
|
||||
! parameters
|
||||
!
|
||||
! Asynchronous (non-blocking)
|
||||
FUNCTION: int PQresetStart ( PGconn* conn ) ;
|
||||
FUNCTION: PostgresPollingStatusType PQresetPoll ( PGconn* conn ) ;
|
||||
|
||||
! Synchronous (blocking)
|
||||
FUNCTION: void PQreset ( PGconn* conn ) ;
|
||||
|
||||
! request a cancel structure
|
||||
FUNCTION: PGcancel* PQgetCancel ( PGconn* conn ) ;
|
||||
|
||||
! free a cancel structure
|
||||
FUNCTION: void PQfreeCancel ( PGcancel* cancel ) ;
|
||||
|
||||
! issue a cancel request
|
||||
FUNCTION: int PQrequestCancel ( PGconn* conn ) ;
|
||||
|
||||
! Accessor functions for PGconn objects
|
||||
FUNCTION: char* PQdb ( PGconn* conn ) ;
|
||||
FUNCTION: char* PQuser ( PGconn* conn ) ;
|
||||
FUNCTION: char* PQpass ( PGconn* conn ) ;
|
||||
FUNCTION: char* PQhost ( PGconn* conn ) ;
|
||||
FUNCTION: char* PQport ( PGconn* conn ) ;
|
||||
FUNCTION: char* PQtty ( PGconn* conn ) ;
|
||||
FUNCTION: char* PQoptions ( PGconn* conn ) ;
|
||||
FUNCTION: ConnStatusType PQstatus ( PGconn* conn ) ;
|
||||
FUNCTION: PGTransactionStatusType PQtransactionStatus ( PGconn* conn ) ;
|
||||
FUNCTION: char* PQparameterStatus ( PGconn* conn,
|
||||
char* paramName ) ;
|
||||
FUNCTION: int PQprotocolVersion ( PGconn* conn ) ;
|
||||
! FUNCTION: int PQServerVersion ( PGconn* conn ) ;
|
||||
FUNCTION: char* PQerrorMessage ( PGconn* conn ) ;
|
||||
FUNCTION: int PQsocket ( PGconn* conn ) ;
|
||||
FUNCTION: int PQbackendPID ( PGconn* conn ) ;
|
||||
FUNCTION: int PQclientEncoding ( PGconn* conn ) ;
|
||||
FUNCTION: int PQsetClientEncoding ( PGconn* conn, char* encoding ) ;
|
||||
|
||||
! May not be compiled into libpq
|
||||
! Get the SSL structure associated with a connection
|
||||
FUNCTION: SSL* PQgetssl ( PGconn* conn ) ;
|
||||
|
||||
! Tell libpq whether it needs to initialize OpenSSL
|
||||
FUNCTION: void PQinitSSL ( int do_init ) ;
|
||||
|
||||
! Set verbosity for PQerrorMessage and PQresultErrorMessage
|
||||
FUNCTION: PGVerbosity PQsetErrorVerbosity ( PGconn* conn,
|
||||
PGVerbosity verbosity ) ;
|
||||
|
||||
! Enable/disable tracing
|
||||
FUNCTION: void PQtrace ( PGconn* conn, FILE* debug_port ) ;
|
||||
FUNCTION: void PQuntrace ( PGconn* conn ) ;
|
||||
|
||||
! BROKEN
|
||||
! Function types for notice-handling callbacks
|
||||
! typedef void (*PQnoticeReceiver) (void *arg, PGresult *res);
|
||||
! typedef void (*PQnoticeProcessor) (void *arg, char* message);
|
||||
! ALIAS: void* PQnoticeReceiver
|
||||
! ALIAS: void* PQnoticeProcessor
|
||||
|
||||
! Override default notice handling routines
|
||||
! FUNCTION: PQnoticeReceiver PQsetNoticeReceiver ( PGconn* conn,
|
||||
! PQnoticeReceiver proc,
|
||||
! void* arg ) ;
|
||||
! FUNCTION: PQnoticeProcessor PQsetNoticeProcessor ( PGconn* conn,
|
||||
! PQnoticeProcessor proc,
|
||||
! void* arg ) ;
|
||||
! END BROKEN
|
||||
|
||||
! === in fe-exec.c ===
|
||||
|
||||
! Simple synchronous query
|
||||
FUNCTION: PGresult* PQexec ( PGconn* conn, char* query ) ;
|
||||
FUNCTION: PGresult* PQexecParams ( PGconn* conn,
|
||||
char* command,
|
||||
int nParams,
|
||||
Oid* paramTypes,
|
||||
char** paramValues,
|
||||
int* paramLengths,
|
||||
int* paramFormats,
|
||||
int resultFormat ) ;
|
||||
FUNCTION: PGresult* PQprepare ( PGconn* conn, char* stmtName,
|
||||
char* query, int nParams,
|
||||
Oid* paramTypes ) ;
|
||||
FUNCTION: PGresult* PQexecPrepared ( PGconn* conn,
|
||||
char* stmtName,
|
||||
int nParams,
|
||||
char** paramValues,
|
||||
int* paramLengths,
|
||||
int* paramFormats,
|
||||
int resultFormat ) ;
|
||||
|
||||
! Interface for multiple-result or asynchronous queries
|
||||
FUNCTION: int PQsendQuery ( PGconn* conn, char* query ) ;
|
||||
FUNCTION: int PQsendQueryParams ( PGconn* conn,
|
||||
char* command,
|
||||
int nParams,
|
||||
Oid* paramTypes,
|
||||
char** paramValues,
|
||||
int* paramLengths,
|
||||
int* paramFormats,
|
||||
int resultFormat ) ;
|
||||
FUNCTION: PGresult* PQsendPrepare ( PGconn* conn, char* stmtName,
|
||||
char* query, int nParams,
|
||||
Oid* paramTypes ) ;
|
||||
FUNCTION: int PQsendQueryPrepared ( PGconn* conn,
|
||||
char* stmtName,
|
||||
int nParams,
|
||||
char** paramValues,
|
||||
int *paramLengths,
|
||||
int *paramFormats,
|
||||
int resultFormat ) ;
|
||||
FUNCTION: PGresult* PQgetResult ( PGconn* conn ) ;
|
||||
|
||||
! Routines for managing an asynchronous query
|
||||
FUNCTION: int PQisBusy ( PGconn* conn ) ;
|
||||
FUNCTION: int PQconsumeInput ( PGconn* conn ) ;
|
||||
|
||||
! LISTEN/NOTIFY support
|
||||
FUNCTION: PGnotify* PQnotifies ( PGconn* conn ) ;
|
||||
|
||||
! Routines for copy in/out
|
||||
FUNCTION: int PQputCopyData ( PGconn* conn, char* buffer, int nbytes ) ;
|
||||
FUNCTION: int PQputCopyEnd ( PGconn* conn, char* errormsg ) ;
|
||||
FUNCTION: int PQgetCopyData ( PGconn* conn, char** buffer, int async ) ;
|
||||
|
||||
! Deprecated routines for copy in/out
|
||||
FUNCTION: int PQgetline ( PGconn* conn, char* string, int length ) ;
|
||||
FUNCTION: int PQputline ( PGconn* conn, char* string ) ;
|
||||
FUNCTION: int PQgetlineAsync ( PGconn* conn, char* buffer, int bufsize ) ;
|
||||
FUNCTION: int PQputnbytes ( PGconn* conn, char* buffer, int nbytes ) ;
|
||||
FUNCTION: int PQendcopy ( PGconn* conn ) ;
|
||||
|
||||
! Set blocking/nonblocking connection to the backend
|
||||
FUNCTION: int PQsetnonblocking ( PGconn* conn, int arg ) ;
|
||||
FUNCTION: int PQisnonblocking ( PGconn* conn ) ;
|
||||
|
||||
! Force the write buffer to be written (or at least try)
|
||||
FUNCTION: int PQflush ( PGconn* conn ) ;
|
||||
|
||||
!
|
||||
! * "Fast path" interface --- not really recommended for application
|
||||
! * use
|
||||
!
|
||||
FUNCTION: PGresult* PQfn ( PGconn* conn,
|
||||
int fnid,
|
||||
int* result_buf,
|
||||
int* result_len,
|
||||
int result_is_int,
|
||||
PQArgBlock* args,
|
||||
int nargs ) ;
|
||||
|
||||
! Accessor functions for PGresult objects
|
||||
FUNCTION: ExecStatusType PQresultStatus ( PGresult* res ) ;
|
||||
FUNCTION: char* PQresStatus ( ExecStatusType status ) ;
|
||||
FUNCTION: char* PQresultErrorMessage ( PGresult* res ) ;
|
||||
FUNCTION: char* PQresultErrorField ( PGresult* res, int fieldcode ) ;
|
||||
FUNCTION: int PQntuples ( PGresult* res ) ;
|
||||
FUNCTION: int PQnfields ( PGresult* res ) ;
|
||||
FUNCTION: int PQbinaryTuples ( PGresult* res ) ;
|
||||
FUNCTION: char* PQfname ( PGresult* res, int field_num ) ;
|
||||
FUNCTION: int PQfnumber ( PGresult* res, char* field_name ) ;
|
||||
FUNCTION: Oid PQftable ( PGresult* res, int field_num ) ;
|
||||
FUNCTION: int PQftablecol ( PGresult* res, int field_num ) ;
|
||||
FUNCTION: int PQfformat ( PGresult* res, int field_num ) ;
|
||||
FUNCTION: Oid PQftype ( PGresult* res, int field_num ) ;
|
||||
FUNCTION: int PQfsize ( PGresult* res, int field_num ) ;
|
||||
FUNCTION: int PQfmod ( PGresult* res, int field_num ) ;
|
||||
FUNCTION: char* PQcmdStatus ( PGresult* res ) ;
|
||||
FUNCTION: char* PQoidStatus ( PGresult* res ) ;
|
||||
FUNCTION: Oid PQoidValue ( PGresult* res ) ;
|
||||
FUNCTION: char* PQcmdTuples ( PGresult* res ) ;
|
||||
FUNCTION: char* PQgetvalue ( PGresult* res, int tup_num, int field_num ) ;
|
||||
FUNCTION: int PQgetlength ( PGresult* res, int tup_num, int field_num ) ;
|
||||
FUNCTION: int PQgetisnull ( PGresult* res, int tup_num, int field_num ) ;
|
||||
|
||||
! Delete a PGresult
|
||||
FUNCTION: void PQclear ( PGresult* res ) ;
|
||||
|
||||
! For freeing other alloc'd results, such as PGnotify structs
|
||||
FUNCTION: void PQfreemem ( void* ptr ) ;
|
||||
|
||||
! Exists for backward compatibility.
|
||||
: PQfreeNotify PQfreemem ;
|
||||
|
||||
!
|
||||
! Make an empty PGresult with given status (some apps find this
|
||||
! useful). If conn is not NULL and status indicates an error, the
|
||||
! conn's errorMessage is copied.
|
||||
!
|
||||
FUNCTION: PGresult* PQmakeEmptyPGresult ( PGconn* conn, ExecStatusType status ) ;
|
||||
|
||||
! Quoting strings before inclusion in queries.
|
||||
FUNCTION: size_t PQescapeStringConn ( PGconn* conn,
|
||||
char* to, char* from, size_t length,
|
||||
int* error ) ;
|
||||
FUNCTION: uchar* PQescapeByteaConn ( PGconn* conn,
|
||||
char* from, size_t length,
|
||||
size_t* to_length ) ;
|
||||
FUNCTION: uchar* PQunescapeBytea ( uchar* strtext,
|
||||
size_t* retbuflen ) ;
|
||||
! These forms are deprecated!
|
||||
FUNCTION: size_t PQescapeString ( void* to, char* from, size_t length ) ;
|
||||
FUNCTION: uchar* PQescapeBytea ( uchar* bintext, size_t binlen,
|
||||
size_t* bytealen ) ;
|
||||
|
||||
! === in fe-print.c ===
|
||||
|
||||
FUNCTION: void PQprint ( FILE* fout, PGresult* res, PQprintOpt* ps ) ;
|
||||
|
||||
! really old printing routines
|
||||
FUNCTION: void PQdisplayTuples ( PGresult* res,
|
||||
FILE* fp,
|
||||
int fillAlign,
|
||||
char* fieldSep,
|
||||
int printHeader,
|
||||
int quiet ) ;
|
||||
|
||||
FUNCTION: void PQprintTuples ( PGresult* res,
|
||||
FILE* fout,
|
||||
int printAttName,
|
||||
int terseOutput,
|
||||
int width ) ;
|
||||
|
||||
! === in fe-lobj.c ===
|
||||
|
||||
! Large-object access routines
|
||||
FUNCTION: int lo_open ( PGconn* conn, Oid lobjId, int mode ) ;
|
||||
FUNCTION: int lo_close ( PGconn* conn, int fd ) ;
|
||||
FUNCTION: int lo_read ( PGconn* conn, int fd, char* buf, size_t len ) ;
|
||||
FUNCTION: int lo_write ( PGconn* conn, int fd, char* buf, size_t len ) ;
|
||||
FUNCTION: int lo_lseek ( PGconn* conn, int fd, int offset, int whence ) ;
|
||||
FUNCTION: Oid lo_creat ( PGconn* conn, int mode ) ;
|
||||
! FUNCTION: Oid lo_creat ( PGconn* conn, Oid lobjId ) ;
|
||||
FUNCTION: int lo_tell ( PGconn* conn, int fd ) ;
|
||||
FUNCTION: int lo_unlink ( PGconn* conn, Oid lobjId ) ;
|
||||
FUNCTION: Oid lo_import ( PGconn* conn, char* filename ) ;
|
||||
FUNCTION: int lo_export ( PGconn* conn, Oid lobjId, char* filename ) ;
|
||||
|
||||
! === in fe-misc.c ===
|
||||
|
||||
! Determine length of multibyte encoded char at *s
|
||||
FUNCTION: int PQmblen ( uchar* s, int encoding ) ;
|
||||
|
||||
! Determine display length of multibyte encoded char at *s
|
||||
FUNCTION: int PQdsplen ( uchar* s, int encoding ) ;
|
||||
|
||||
! Get encoding id from environment variable PGCLIENTENCODING
|
||||
FUNCTION: int PQenv2encoding ( ) ;
|
|
@ -0,0 +1,72 @@
|
|||
USING: arrays continuations db io kernel math namespaces
|
||||
quotations sequences db.postgresql.ffi ;
|
||||
IN: db.postgresql.lib
|
||||
|
||||
SYMBOL: query-res
|
||||
|
||||
: connect-postgres ( host port pgopts pgtty db user pass -- conn )
|
||||
PQsetdbLogin
|
||||
dup PQstatus zero? [ "couldn't connect to database" throw ] unless ;
|
||||
|
||||
: postgresql-result-error-message ( res -- str/f )
|
||||
dup zero? [
|
||||
drop f
|
||||
] [
|
||||
PQresultErrorMessage [ CHAR: \n = ] right-trim
|
||||
] if ;
|
||||
|
||||
: postgres-result-error ( res -- )
|
||||
postgresql-result-error-message [ throw ] when* ;
|
||||
|
||||
: postgresql-error-message ( -- str )
|
||||
db get db-handle PQerrorMessage [ CHAR: \n = ] right-trim ;
|
||||
|
||||
: postgresql-error ( res -- res )
|
||||
dup [ postgresql-error-message throw ] unless ;
|
||||
|
||||
: postgresql-result-ok? ( n -- ? )
|
||||
PQresultStatus
|
||||
PGRES_COMMAND_OK PGRES_TUPLES_OK 2array member? ;
|
||||
|
||||
: do-postgresql-statement ( statement -- res )
|
||||
db get db-handle swap statement-sql PQexec dup postgresql-result-ok? [
|
||||
dup postgresql-result-error-message swap PQclear throw
|
||||
] unless ;
|
||||
|
||||
! : do-command ( str -- )
|
||||
! 1quotation \ (do-command) add db get swap call ;
|
||||
|
||||
! : prepare ( str quot word -- conn quot )
|
||||
! rot 1quotation swap append swap append db get swap ;
|
||||
|
||||
! : do-query ( str quot -- )
|
||||
! [ (do-query) query-res set ] prepare catch
|
||||
! [ rethrow ] [ query-res get PQclear ] if* ;
|
||||
|
||||
! : result>seq ( -- seq )
|
||||
! query-res get [ PQnfields ] keep PQntuples
|
||||
! [ swap [ query-res get -rot PQgetvalue ] with map ] with map ;
|
||||
!
|
||||
! : print-table ( seq -- )
|
||||
! [ [ write bl ] each "\n" write ] each ;
|
||||
|
||||
|
||||
|
||||
! select * from animal where name = 'Simba'
|
||||
! select * from animal where name = $1
|
||||
|
||||
! : (do-query) ( PGconn query -- PGresult* )
|
||||
! ! For queries that do not return rows, PQexec() returns PGRES_COMMAND_OK
|
||||
! ! For queries that return rows, PQexec() returns PGRES_TUPLES_OK
|
||||
! PQexec dup postgresql-result-ok? [
|
||||
! dup postgresql-error-message swap PQclear throw
|
||||
! ] unless ;
|
||||
|
||||
! : (do-command) ( PGconn query -- PGresult* )
|
||||
! [ (do-query) ] catch
|
||||
! [
|
||||
! swap
|
||||
! "non-fatal error: " print
|
||||
! "\tQuery: " write "'" write write "'" print
|
||||
! "\t" write print
|
||||
! ] when* drop ;
|
|
@ -0,0 +1,54 @@
|
|||
! You will need to run 'createdb factor-test' to create the database.
|
||||
! Set username and password in the 'connect' word.
|
||||
|
||||
USING: kernel db.postgresql alien continuations io prettyprint
|
||||
sequences namespaces tools.test ;
|
||||
IN: temporary
|
||||
|
||||
: test-connection ( host port pgopts pgtty db user pass -- bool )
|
||||
[ [ ] with-postgres ] catch "Error connecting!" "Connected!" ? print ;
|
||||
|
||||
[ ] [ "localhost" "" "" "" "factor-test" "postgres" "" test-connection ] unit-test
|
||||
|
||||
[ ] [ "localhost" "postgres" "" "factor-test" <postgresql-db> [ ] with-db ] unit-test
|
||||
|
||||
! just a basic demo
|
||||
|
||||
"localhost" "postgres" "" "factor-test" <postgresql-db> [
|
||||
[ ] [ "drop table animal" do-command ] unit-test
|
||||
|
||||
[ ] [ "create table animal (id serial not null primary key, species varchar(256), name varchar(256), age integer)" do-command ] unit-test
|
||||
|
||||
[ ] [ "insert into animal (species, name, age) values ('lion', 'Mufasa', 5)"
|
||||
do-command ] unit-test
|
||||
|
||||
[ ] [ "select * from animal where name = 'Mufasa'" [ ] do-query ] unit-test
|
||||
[ ] [ "select * from animal where name = 'Mufasa'" [
|
||||
result>seq length 1 = [
|
||||
"...there can only be one Mufasa..." throw
|
||||
] unless
|
||||
] do-query
|
||||
] unit-test
|
||||
|
||||
[ ] [ "insert into animal (species, name, age) values ('lion', 'Simba', 1)"
|
||||
do-command ] unit-test
|
||||
|
||||
[ ] [
|
||||
"select * from animal"
|
||||
[
|
||||
"Animal table:" print
|
||||
result>seq print-table
|
||||
] do-query
|
||||
] unit-test
|
||||
|
||||
! intentional errors
|
||||
! [ "select asdf from animal"
|
||||
! [ ] do-query ] catch [ "caught: " write print ] when*
|
||||
! "select asdf from animal" [ ] do-query
|
||||
! "aofijweafew" do-command
|
||||
] with-db
|
||||
|
||||
|
||||
"localhost" "postgres" "" "factor-test" <postgresql-db> [
|
||||
[ ] [ "drop table animal" do-command ] unit-test
|
||||
] with-db
|
|
@ -0,0 +1,87 @@
|
|||
! Copyright (C) 2007 Doug Coleman.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
! adapted from libpq-fe.h version 7.4.7
|
||||
! tested on debian linux with postgresql 7.4.7
|
||||
|
||||
USING: arrays assocs alien alien.syntax continuations io
|
||||
kernel math namespaces prettyprint quotations
|
||||
sequences debugger db db.postgresql.lib db.postgresql.ffi ;
|
||||
IN: db.postgresql
|
||||
|
||||
TUPLE: postgresql-db host port pgopts pgtty db user pass ;
|
||||
TUPLE: postgresql-statement ;
|
||||
: <postgresql-statement> ( statement -- postgresql-statement )
|
||||
postgresql-statement construct-delegate ;
|
||||
|
||||
: <postgresql-db> ( host user pass db -- obj )
|
||||
{
|
||||
set-postgresql-db-host
|
||||
set-postgresql-db-user
|
||||
set-postgresql-db-pass
|
||||
set-postgresql-db-db
|
||||
} postgresql-db construct ;
|
||||
|
||||
M: postgresql-db db-open ( db -- )
|
||||
dup {
|
||||
postgresql-db-host
|
||||
postgresql-db-port
|
||||
postgresql-db-pgopts
|
||||
postgresql-db-pgtty
|
||||
postgresql-db-db
|
||||
postgresql-db-user
|
||||
postgresql-db-pass
|
||||
} get-slots connect-postgres <db> swap set-delegate ;
|
||||
|
||||
M: postgresql-db dispose ( db -- )
|
||||
db-handle PQfinish ;
|
||||
|
||||
: with-postgresql ( host ust pass db quot -- )
|
||||
>r <postgresql-db> r> with-disposal ;
|
||||
|
||||
M: postgresql-statement #rows ( statement -- n )
|
||||
statement-handle PQntuples ;
|
||||
|
||||
M: postgresql-statement #columns ( statement -- n )
|
||||
statement-handle PQnfields ;
|
||||
|
||||
M: postgresql-statement row-column ( statement n -- obj )
|
||||
>r dup statement-handle swap statement-n r> PQgetvalue ;
|
||||
|
||||
: init-statement ( statement -- )
|
||||
dup statement-max [
|
||||
dup do-postgresql-statement over set-statement-handle
|
||||
dup #rows over set-statement-max
|
||||
-1 over set-statement-n
|
||||
] unless drop ;
|
||||
|
||||
: increment-n ( statement -- n )
|
||||
dup statement-n 1+ dup rot set-statement-n ;
|
||||
|
||||
M: postgresql-statement advance-row ( statement -- ? )
|
||||
dup init-statement
|
||||
dup increment-n swap statement-max >= ;
|
||||
|
||||
M: postgresql-statement dispose ( query -- )
|
||||
dup statement-handle PQclear
|
||||
0 0 rot { set-statement-n set-statement-max } set-slots ;
|
||||
|
||||
M: postgresql-statement prepare-statement ( statement -- )
|
||||
[
|
||||
>r db get db-handle "" r>
|
||||
dup statement-sql swap statement-params
|
||||
dup assoc-size swap PQprepare postgresql-error
|
||||
] keep set-statement-handle ;
|
||||
|
||||
M: postgresql-db <simple-statement> ( sql -- statement )
|
||||
{ set-statement-sql } statement construct
|
||||
<postgresql-statement> ;
|
||||
|
||||
M: postgresql-db <bound-statement> ( sql array -- statement )
|
||||
{ set-statement-sql set-statement-params } statement construct
|
||||
<postgresql-statement> ;
|
||||
|
||||
M: postgresql-db <prepared-statement> ( sql -- statement )
|
||||
;
|
||||
|
||||
M: postgresql-db <prepared-bound-statement> ( sql seq -- statement )
|
||||
;
|
|
@ -0,0 +1,2 @@
|
|||
Chris Double
|
||||
Doug Coleman
|
|
@ -0,0 +1,131 @@
|
|||
! Copyright (C) 2005 Chris Double, Doug Coleman.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
!
|
||||
! An interface to the sqlite database. Tested against sqlite v3.1.3.
|
||||
|
||||
! Not all functions have been wrapped yet. Only those directly involving
|
||||
! executing SQL calls and obtaining results.
|
||||
|
||||
USING: alien compiler kernel math namespaces sequences strings alien.syntax
|
||||
system combinators ;
|
||||
IN: db.sqlite.ffi
|
||||
|
||||
<<
|
||||
"sqlite" {
|
||||
{ [ winnt? ] [ "sqlite3.dll" ] }
|
||||
{ [ macosx? ] [ "/usr/lib/libsqlite3.dylib" ] }
|
||||
{ [ unix? ] [ "libsqlite3.so" ] }
|
||||
} cond "cdecl" add-library >>
|
||||
|
||||
! Return values from sqlite functions
|
||||
: SQLITE_OK 0 ; inline ! Successful result
|
||||
: SQLITE_ERROR 1 ; inline ! SQL error or missing database
|
||||
: SQLITE_INTERNAL 2 ; inline ! An internal logic error in SQLite
|
||||
: SQLITE_PERM 3 ; inline ! Access permission denied
|
||||
: SQLITE_ABORT 4 ; inline ! Callback routine requested an abort
|
||||
: SQLITE_BUSY 5 ; inline ! The database file is locked
|
||||
: SQLITE_LOCKED 6 ; inline ! A table in the database is locked
|
||||
: SQLITE_NOMEM 7 ; inline ! A malloc() failed
|
||||
: SQLITE_READONLY 8 ; inline ! Attempt to write a readonly database
|
||||
: SQLITE_INTERRUPT 9 ; inline ! Operation terminated by sqlite_interrupt()
|
||||
: SQLITE_IOERR 10 ; inline ! Some kind of disk I/O error occurred
|
||||
: SQLITE_CORRUPT 11 ; inline ! The database disk image is malformed
|
||||
: SQLITE_NOTFOUND 12 ; inline ! (Internal Only) Table or record not found
|
||||
: SQLITE_FULL 13 ; inline ! Insertion failed because database is full
|
||||
: SQLITE_CANTOPEN 14 ; inline ! Unable to open the database file
|
||||
: SQLITE_PROTOCOL 15 ; inline ! Database lock protocol error
|
||||
: SQLITE_EMPTY 16 ; inline ! (Internal Only) Database table is empty
|
||||
: SQLITE_SCHEMA 17 ; inline ! The database schema changed
|
||||
: SQLITE_TOOBIG 18 ; inline ! Too much data for one row of a table
|
||||
: SQLITE_CONSTRAINT 19 ; inline ! Abort due to contraint violation
|
||||
: SQLITE_MISMATCH 20 ; inline ! Data type mismatch
|
||||
: SQLITE_MISUSE 21 ; inline ! Library used incorrectly
|
||||
: SQLITE_NOLFS 22 ; inline ! Uses OS features not supported on host
|
||||
: SQLITE_AUTH 23 ; inline ! Authorization denied
|
||||
: SQLITE_FORMAT 24 ; inline ! Auxiliary database format error
|
||||
: SQLITE_RANGE 25 ; inline ! 2nd parameter to sqlite3_bind out of range
|
||||
: SQLITE_NOTADB 26 ; inline ! File opened that is not a database file
|
||||
|
||||
: sqlite-error-messages ( -- seq ) {
|
||||
"Successful result"
|
||||
"SQL error or missing database"
|
||||
"An internal logic error in SQLite"
|
||||
"Access permission denied"
|
||||
"Callback routine requested an abort"
|
||||
"The database file is locked"
|
||||
"A table in the database is locked"
|
||||
"A malloc() failed"
|
||||
"Attempt to write a readonly database"
|
||||
"Operation terminated by sqlite_interrupt()"
|
||||
"Some kind of disk I/O error occurred"
|
||||
"The database disk image is malformed"
|
||||
"(Internal Only) Table or record not found"
|
||||
"Insertion failed because database is full"
|
||||
"Unable to open the database file"
|
||||
"Database lock protocol error"
|
||||
"(Internal Only) Database table is empty"
|
||||
"The database schema changed"
|
||||
"Too much data for one row of a table"
|
||||
"Abort due to contraint violation"
|
||||
"Data type mismatch"
|
||||
"Library used incorrectly"
|
||||
"Uses OS features not supported on host"
|
||||
"Authorization denied"
|
||||
"Auxiliary database format error"
|
||||
"2nd parameter to sqlite3_bind out of range"
|
||||
"File opened that is not a database file"
|
||||
} ;
|
||||
|
||||
: SQLITE_ROW 100 ; inline ! sqlite_step() has another row ready
|
||||
: SQLITE_DONE 101 ; inline ! sqlite_step() has finished executing
|
||||
|
||||
! Return values from the sqlite3_column_type function
|
||||
: SQLITE_INTEGER 1 ; inline
|
||||
: SQLITE_FLOAT 2 ; inline
|
||||
: SQLITE_TEXT 3 ; inline
|
||||
: SQLITE_BLOB 4 ; inline
|
||||
: SQLITE_NULL 5 ; inline
|
||||
|
||||
! Values for the 'destructor' parameter of the 'bind' routines.
|
||||
: SQLITE_STATIC 0 ; inline
|
||||
: SQLITE_TRANSIENT -1 ; inline
|
||||
|
||||
: SQLITE_OPEN_READONLY HEX: 00000001 ; inline
|
||||
: SQLITE_OPEN_READWRITE HEX: 00000002 ; inline
|
||||
: SQLITE_OPEN_CREATE HEX: 00000004 ; inline
|
||||
: SQLITE_OPEN_DELETEONCLOSE HEX: 00000008 ; inline
|
||||
: SQLITE_OPEN_EXCLUSIVE HEX: 00000010 ; inline
|
||||
: SQLITE_OPEN_MAIN_DB HEX: 00000100 ; inline
|
||||
: SQLITE_OPEN_TEMP_DB HEX: 00000200 ; inline
|
||||
: SQLITE_OPEN_TRANSIENT_DB HEX: 00000400 ; inline
|
||||
: SQLITE_OPEN_MAIN_JOURNAL HEX: 00000800 ; inline
|
||||
: SQLITE_OPEN_TEMP_JOURNAL HEX: 00001000 ; inline
|
||||
: SQLITE_OPEN_SUBJOURNAL HEX: 00002000 ; inline
|
||||
: SQLITE_OPEN_MASTER_JOURNAL HEX: 00004000 ; inline
|
||||
|
||||
|
||||
TYPEDEF: void sqlite3
|
||||
TYPEDEF: void sqlite3_stmt
|
||||
|
||||
LIBRARY: sqlite
|
||||
FUNCTION: int sqlite3_open ( char* filename, void* ppDb ) ;
|
||||
FUNCTION: int sqlite3_open_v2 ( char* filename, void* ppDb, int flags, char* zVfs ) ;
|
||||
FUNCTION: int sqlite3_close ( sqlite3* pDb ) ;
|
||||
FUNCTION: int sqlite3_prepare ( sqlite3* pDb, char* zSql, int nBytes, void* ppStmt, void* pzTail ) ;
|
||||
FUNCTION: int sqlite3_finalize ( sqlite3_stmt* pStmt ) ;
|
||||
FUNCTION: int sqlite3_reset ( sqlite3_stmt* pStmt ) ;
|
||||
FUNCTION: int sqlite3_step ( sqlite3_stmt* pStmt ) ;
|
||||
FUNCTION: int sqlite3_last_insert_rowid ( sqlite3* pStmt ) ;
|
||||
FUNCTION: int sqlite3_bind_blob ( sqlite3_stmt* pStmt, int index, void* ptr, int len, int destructor ) ;
|
||||
FUNCTION: int sqlite3_bind_int ( sqlite3_stmt* pStmt, int index, int n ) ;
|
||||
FUNCTION: int sqlite3_bind_null ( sqlite3_stmt* pStmt, int n ) ;
|
||||
FUNCTION: int sqlite3_bind_text ( sqlite3_stmt* pStmt, int index, char* text, int len, int destructor ) ;
|
||||
FUNCTION: int sqlite3_bind_parameter_index ( sqlite3_stmt* pStmt, char* name ) ;
|
||||
FUNCTION: int sqlite3_column_count ( sqlite3_stmt* pStmt ) ;
|
||||
FUNCTION: void* sqlite3_column_blob ( sqlite3_stmt* pStmt, int col ) ;
|
||||
FUNCTION: int sqlite3_column_bytes ( sqlite3_stmt* pStmt, int col ) ;
|
||||
FUNCTION: char* sqlite3_column_decltype ( sqlite3_stmt* pStmt, int col ) ;
|
||||
FUNCTION: int sqlite3_column_int ( sqlite3_stmt* pStmt, int col ) ;
|
||||
FUNCTION: int sqlite3_column_name ( sqlite3_stmt* pStmt, int col ) ;
|
||||
FUNCTION: char* sqlite3_column_text ( sqlite3_stmt* pStmt, int col ) ;
|
||||
FUNCTION: int sqlite3_column_type ( sqlite3_stmt* pStmt, int col ) ;
|
|
@ -0,0 +1,103 @@
|
|||
USING: alien.c-types assocs kernel math math.parser sequences
|
||||
db.sqlite.ffi ;
|
||||
IN: db.sqlite.lib
|
||||
|
||||
TUPLE: sqlite-error n message ;
|
||||
|
||||
: sqlite-check-result ( result -- )
|
||||
dup SQLITE_OK = [
|
||||
drop
|
||||
] [
|
||||
dup sqlite-error-messages nth
|
||||
sqlite-error construct-boa throw
|
||||
] if ;
|
||||
|
||||
: sqlite-open ( filename -- db )
|
||||
"void*" <c-object>
|
||||
[ sqlite3_open sqlite-check-result ] keep *void* ;
|
||||
|
||||
: sqlite-close ( db -- )
|
||||
sqlite3_close sqlite-check-result ;
|
||||
|
||||
: sqlite-last-insert-rowid ( db -- rowid )
|
||||
sqlite3_last_insert_rowid ;
|
||||
|
||||
: sqlite-prepare ( db sql -- statement )
|
||||
#! 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 -- )
|
||||
dup number? [ number>string ] when
|
||||
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-bind-assoc ( statement assoc -- )
|
||||
swap [
|
||||
-rot sqlite-bind-text-by-name
|
||||
] curry assoc-each ;
|
||||
|
||||
: sqlite-finalize ( statement -- )
|
||||
sqlite3_finalize sqlite-check-result ;
|
||||
|
||||
: sqlite-reset ( statement -- )
|
||||
sqlite3_reset sqlite-check-result ;
|
||||
|
||||
: sqlite-#columns ( query -- int )
|
||||
sqlite3_column_count ;
|
||||
|
||||
: sqlite-column ( statement index -- string )
|
||||
sqlite3_column_text ;
|
||||
|
||||
: sqlite-row ( statement -- seq )
|
||||
dup sqlite-#columns [ sqlite-column ] with map ;
|
||||
|
||||
! 2dup sqlite3_column_type .
|
||||
! SQLITE_INTEGER 1
|
||||
! SQLITE_FLOAT 2
|
||||
! SQLITE_TEXT 3
|
||||
! SQLITE_BLOB 4
|
||||
! SQLITE_NULL 5
|
||||
|
||||
|
||||
: step-complete? ( step-result -- bool )
|
||||
dup SQLITE_ROW = [
|
||||
drop f
|
||||
] [
|
||||
dup SQLITE_DONE = [ drop t ] [ sqlite-check-result t ] if
|
||||
] if ;
|
||||
|
||||
: sqlite-step ( prepared -- )
|
||||
dup sqlite3_step step-complete? [
|
||||
drop
|
||||
] [
|
||||
sqlite-step
|
||||
] if ;
|
||||
|
||||
: sqlite-next ( prepared -- )
|
||||
sqlite3_step step-complete? ;
|
||||
|
||||
: sqlite-each ( statement quot -- )
|
||||
over sqlite3_step step-complete? [
|
||||
2drop
|
||||
] [
|
||||
[ call ] 2keep sqlite-each
|
||||
] if ; inline
|
||||
|
||||
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) ;
|
|
@ -0,0 +1,99 @@
|
|||
USING: io io.files io.launcher kernel namespaces
|
||||
prettyprint tools.test db.sqlite db db.sql sequences
|
||||
continuations ;
|
||||
IN: temporary
|
||||
|
||||
! "sqlite3 -init test.txt test.db"
|
||||
|
||||
: test.db "extra/db/sqlite/test.db" resource-path ;
|
||||
|
||||
: (create-db) ( -- str )
|
||||
[
|
||||
"sqlite3 -init " %
|
||||
"extra/db/sqlite/test.txt" resource-path %
|
||||
" " %
|
||||
test.db %
|
||||
] "" make ;
|
||||
|
||||
: create-db ( -- ) (create-db) run-process drop ;
|
||||
|
||||
[ ] [ test.db delete-file ] unit-test
|
||||
|
||||
[ ] [ create-db ] unit-test
|
||||
|
||||
[
|
||||
{
|
||||
{ "John" "America" }
|
||||
{ "Jane" "New Zealand" }
|
||||
}
|
||||
] [ test.db [ "select * from person" do-simple-query ] with-sqlite ] unit-test
|
||||
|
||||
[
|
||||
{ { "John" "America" } }
|
||||
] [
|
||||
test.db [
|
||||
"select * from person where name = :name and country = :country"
|
||||
{ { ":name" "Jane" } { ":country" "New Zealand" } }
|
||||
<bound-statement> dup [ sql-row ] query-map
|
||||
|
||||
{ { "Jane" "New Zealand" } } = [ "test fails" throw ] unless
|
||||
{ { ":name" "John" } { ":country" "America" } } over bind-statement
|
||||
|
||||
dup [ sql-row ] query-map swap dispose
|
||||
] with-sqlite
|
||||
] unit-test
|
||||
|
||||
[
|
||||
{
|
||||
{ "1" "John" "America" }
|
||||
{ "2" "Jane" "New Zealand" }
|
||||
}
|
||||
] [ test.db [ "select rowid, * from person" do-simple-query ] with-sqlite ] unit-test
|
||||
|
||||
[
|
||||
] [
|
||||
"extra/db/sqlite/test.db" resource-path [
|
||||
"insert into person(name, country) values('Jimmy', 'Canada')"
|
||||
do-simple-command
|
||||
] with-sqlite
|
||||
] unit-test
|
||||
|
||||
[
|
||||
{
|
||||
{ "1" "John" "America" }
|
||||
{ "2" "Jane" "New Zealand" }
|
||||
{ "3" "Jimmy" "Canada" }
|
||||
}
|
||||
] [ test.db [ "select rowid, * from person" do-simple-query ] with-sqlite ] unit-test
|
||||
|
||||
[
|
||||
"extra/db/sqlite/test.db" resource-path [
|
||||
[
|
||||
"insert into person(name, country) values('Jose', 'Mexico')" do-simple-command
|
||||
"insert into person(name, country) values('Jose', 'Mexico')" do-simple-command
|
||||
"oops" throw
|
||||
] with-transaction
|
||||
] with-sqlite
|
||||
] unit-test-fails
|
||||
|
||||
[ 3 ] [
|
||||
"extra/db/sqlite/test.db" resource-path [
|
||||
"select * from person" do-simple-query length
|
||||
] with-sqlite
|
||||
] unit-test
|
||||
|
||||
[
|
||||
] [
|
||||
"extra/db/sqlite/test.db" resource-path [
|
||||
[
|
||||
"insert into person(name, country) values('Jose', 'Mexico')" do-simple-command
|
||||
"insert into person(name, country) values('Jose', 'Mexico')" do-simple-command
|
||||
] with-transaction
|
||||
] with-sqlite
|
||||
] unit-test
|
||||
|
||||
[ 5 ] [
|
||||
"extra/db/sqlite/test.db" resource-path [
|
||||
"select * from person" do-simple-query length
|
||||
] with-sqlite
|
||||
] unit-test
|
|
@ -0,0 +1,70 @@
|
|||
! Copyright (C) 2005, 2008 Chris Double, Doug Coleman.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: alien arrays assocs classes compiler db db.sql hashtables
|
||||
io.files kernel math math.parser namespaces prettyprint sequences
|
||||
strings sqlite.lib tuples alien.c-types continuations
|
||||
db.sqlite.lib db.sqlite.ffi ;
|
||||
IN: db.sqlite
|
||||
|
||||
TUPLE: sqlite-db path ;
|
||||
C: <sqlite-db> sqlite-db
|
||||
|
||||
M: sqlite-db db-open ( db -- )
|
||||
dup sqlite-db-path sqlite-open <db>
|
||||
swap set-delegate ;
|
||||
|
||||
M: sqlite-db dispose ( obj -- )
|
||||
dup db-handle sqlite-close
|
||||
f over set-db-handle
|
||||
f swap set-delegate ;
|
||||
|
||||
: with-sqlite ( path quot -- )
|
||||
>r <sqlite-db> r> with-db ; inline
|
||||
|
||||
TUPLE: sqlite-statement ;
|
||||
C: <sqlite-statement> sqlite-statement
|
||||
|
||||
M: sqlite-db <simple-statement> ( str -- obj )
|
||||
<prepared-statement> ;
|
||||
|
||||
M: sqlite-db <bound-statement> ( str -- obj )
|
||||
<prepared-bound-statement> ;
|
||||
|
||||
M: sqlite-db <prepared-statement> ( str -- obj )
|
||||
db get db-handle over sqlite-prepare
|
||||
{ set-statement-sql set-statement-handle } statement construct
|
||||
<sqlite-statement> [ set-delegate ] keep ;
|
||||
|
||||
M: sqlite-db <prepared-bound-statement> ( str assoc -- obj )
|
||||
swap <prepared-statement> tuck bind-statement ;
|
||||
|
||||
M: sqlite-statement dispose ( statement -- )
|
||||
statement-handle sqlite-finalize ;
|
||||
|
||||
M: sqlite-statement bind-statement* ( assoc statement -- )
|
||||
statement-handle swap sqlite-bind-assoc ;
|
||||
|
||||
M: sqlite-statement rebind-statement ( assoc statement -- )
|
||||
dup reset-statement
|
||||
statement-handle swap sqlite-bind-assoc ;
|
||||
|
||||
M: sqlite-statement #columns ( statement -- n )
|
||||
statement-handle sqlite-#columns ;
|
||||
|
||||
M: sqlite-statement row-column ( statement n -- obj )
|
||||
>r statement-handle r> sqlite-column ;
|
||||
|
||||
M: sqlite-statement advance-row ( statement -- ? )
|
||||
statement-handle sqlite-next ;
|
||||
|
||||
M: sqlite-statement reset-statement ( statement -- )
|
||||
statement-handle sqlite-reset ;
|
||||
|
||||
M: sqlite-db begin-transaction ( -- )
|
||||
"BEGIN" do-simple-command ;
|
||||
|
||||
M: sqlite-db commit-transaction ( -- )
|
||||
"COMMIT" do-simple-command ;
|
||||
|
||||
M: sqlite-db rollback-transaction ( -- )
|
||||
"ROLLBACK" do-simple-command ;
|
|
@ -0,0 +1,3 @@
|
|||
create table person (name varchar(30), country varchar(30));
|
||||
insert into person values('John', 'America');
|
||||
insert into person values('Jane', 'New Zealand');
|
Loading…
Reference in New Issue