From fd4254ca094f0e8d6134e02f87c661899a98145e Mon Sep 17 00:00:00 2001 From: Doug Coleman Date: Thu, 31 Jan 2008 11:34:03 -0600 Subject: [PATCH 01/16] update client to work with more redirects --- extra/http/client/client.factor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/http/client/client.factor b/extra/http/client/client.factor index 7c385c0bb3..85a8b516ca 100644 --- a/extra/http/client/client.factor +++ b/extra/http/client/client.factor @@ -44,7 +44,7 @@ DEFER: http-get-stream #! Should this support Location: headers that are #! relative URLs? pick 100 /i 3 = [ - stream-close "Location" swap at nip http-get-stream + stream-close "location" swap header-single nip http-get-stream ] when ; : http-get-stream ( url -- code headers stream ) From 71358d3c4aef7bc4946d5655889de2b895152dea Mon Sep 17 00:00:00 2001 From: Doug Coleman Date: Fri, 1 Feb 2008 17:43:44 -0600 Subject: [PATCH 02/16] first commit of db stuff --- extra/db/db.factor | 96 ++++++ extra/db/postgresql/authors.txt | 1 + extra/db/postgresql/ffi/ffi.factor | 360 ++++++++++++++++++++ extra/db/postgresql/lib/lib.factor | 72 ++++ extra/db/postgresql/postgresql-tests.factor | 54 +++ extra/db/postgresql/postgresql.factor | 87 +++++ extra/db/sqlite/authors.txt | 2 + extra/db/sqlite/ffi/ffi.factor | 131 +++++++ extra/db/sqlite/lib/lib.factor | 103 ++++++ extra/db/sqlite/sqlite-tests.factor | 99 ++++++ extra/db/sqlite/sqlite.factor | 70 ++++ extra/db/sqlite/test.txt | 3 + 12 files changed, 1078 insertions(+) create mode 100644 extra/db/db.factor create mode 100644 extra/db/postgresql/authors.txt create mode 100644 extra/db/postgresql/ffi/ffi.factor create mode 100644 extra/db/postgresql/lib/lib.factor create mode 100644 extra/db/postgresql/postgresql-tests.factor create mode 100644 extra/db/postgresql/postgresql.factor create mode 100644 extra/db/sqlite/authors.txt create mode 100644 extra/db/sqlite/ffi/ffi.factor create mode 100644 extra/db/sqlite/lib/lib.factor create mode 100644 extra/db/sqlite/sqlite-tests.factor create mode 100644 extra/db/sqlite/sqlite.factor create mode 100644 extra/db/sqlite/test.txt diff --git a/extra/db/db.factor b/extra/db/db.factor new file mode 100644 index 0000000000..597ac1f0f3 --- /dev/null +++ b/extra/db/db.factor @@ -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 ( 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: db ( str -- statement ) +HOOK: db ( str obj -- statement ) +HOOK: db ( str -- statement ) +HOOK: 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 ) + do-query ; + +: do-bound-query ( sql obj -- rows ) + do-query ; + +: do-simple-command ( sql -- ) + do-statement ; + +: do-bound-command ( sql obj -- ) + 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 ; diff --git a/extra/db/postgresql/authors.txt b/extra/db/postgresql/authors.txt new file mode 100644 index 0000000000..7c1b2f2279 --- /dev/null +++ b/extra/db/postgresql/authors.txt @@ -0,0 +1 @@ +Doug Coleman diff --git a/extra/db/postgresql/ffi/ffi.factor b/extra/db/postgresql/ffi/ffi.factor new file mode 100644 index 0000000000..6d3cdfc468 --- /dev/null +++ b/extra/db/postgresql/ffi/ffi.factor @@ -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 ( ) ; diff --git a/extra/db/postgresql/lib/lib.factor b/extra/db/postgresql/lib/lib.factor new file mode 100644 index 0000000000..4b362f9931 --- /dev/null +++ b/extra/db/postgresql/lib/lib.factor @@ -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 ; diff --git a/extra/db/postgresql/postgresql-tests.factor b/extra/db/postgresql/postgresql-tests.factor new file mode 100644 index 0000000000..438a80e2d8 --- /dev/null +++ b/extra/db/postgresql/postgresql-tests.factor @@ -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" [ ] with-db ] unit-test + +! just a basic demo + +"localhost" "postgres" "" "factor-test" [ + [ ] [ "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" [ + [ ] [ "drop table animal" do-command ] unit-test +] with-db diff --git a/extra/db/postgresql/postgresql.factor b/extra/db/postgresql/postgresql.factor new file mode 100644 index 0000000000..cd2c34682e --- /dev/null +++ b/extra/db/postgresql/postgresql.factor @@ -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 ; +: ( statement -- postgresql-statement ) + postgresql-statement construct-delegate ; + +: ( 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 swap set-delegate ; + +M: postgresql-db dispose ( db -- ) + db-handle PQfinish ; + +: with-postgresql ( host ust pass db quot -- ) + >r 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 ( sql -- statement ) + { set-statement-sql } statement construct + ; + +M: postgresql-db ( sql array -- statement ) + { set-statement-sql set-statement-params } statement construct + ; + +M: postgresql-db ( sql -- statement ) + ; + +M: postgresql-db ( sql seq -- statement ) + ; diff --git a/extra/db/sqlite/authors.txt b/extra/db/sqlite/authors.txt new file mode 100644 index 0000000000..26093b451b --- /dev/null +++ b/extra/db/sqlite/authors.txt @@ -0,0 +1,2 @@ +Chris Double +Doug Coleman diff --git a/extra/db/sqlite/ffi/ffi.factor b/extra/db/sqlite/ffi/ffi.factor new file mode 100644 index 0000000000..77a86a8a2d --- /dev/null +++ b/extra/db/sqlite/ffi/ffi.factor @@ -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 ) ; diff --git a/extra/db/sqlite/lib/lib.factor b/extra/db/sqlite/lib/lib.factor new file mode 100644 index 0000000000..99cd9c1b9f --- /dev/null +++ b/extra/db/sqlite/lib/lib.factor @@ -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*" + [ 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*" "void*" + [ 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) ; diff --git a/extra/db/sqlite/sqlite-tests.factor b/extra/db/sqlite/sqlite-tests.factor new file mode 100644 index 0000000000..79e967de24 --- /dev/null +++ b/extra/db/sqlite/sqlite-tests.factor @@ -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" } } + 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 diff --git a/extra/db/sqlite/sqlite.factor b/extra/db/sqlite/sqlite.factor new file mode 100644 index 0000000000..c5964ed599 --- /dev/null +++ b/extra/db/sqlite/sqlite.factor @@ -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 + +M: sqlite-db db-open ( db -- ) + dup sqlite-db-path sqlite-open + 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 r> with-db ; inline + +TUPLE: sqlite-statement ; +C: sqlite-statement + +M: sqlite-db ( str -- obj ) + ; + +M: sqlite-db ( str -- obj ) + ; + +M: sqlite-db ( str -- obj ) + db get db-handle over sqlite-prepare + { set-statement-sql set-statement-handle } statement construct + [ set-delegate ] keep ; + +M: sqlite-db ( str assoc -- obj ) + swap 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 ; diff --git a/extra/db/sqlite/test.txt b/extra/db/sqlite/test.txt new file mode 100644 index 0000000000..e4487d30f9 --- /dev/null +++ b/extra/db/sqlite/test.txt @@ -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'); From 822e859f9430cd5bc63263fe9630e156ed88b884 Mon Sep 17 00:00:00 2001 From: Doug Coleman Date: Fri, 1 Feb 2008 17:44:15 -0600 Subject: [PATCH 03/16] remove old postgresql --- extra/postgresql/authors.txt | 1 - extra/postgresql/libpq/libpq.factor | 361 ----------------------- extra/postgresql/postgresql-tests.factor | 42 --- extra/postgresql/postgresql.factor | 61 ---- 4 files changed, 465 deletions(-) delete mode 100644 extra/postgresql/authors.txt delete mode 100644 extra/postgresql/libpq/libpq.factor delete mode 100644 extra/postgresql/postgresql-tests.factor delete mode 100644 extra/postgresql/postgresql.factor diff --git a/extra/postgresql/authors.txt b/extra/postgresql/authors.txt deleted file mode 100644 index 7c1b2f2279..0000000000 --- a/extra/postgresql/authors.txt +++ /dev/null @@ -1 +0,0 @@ -Doug Coleman diff --git a/extra/postgresql/libpq/libpq.factor b/extra/postgresql/libpq/libpq.factor deleted file mode 100644 index 3b21fd8203..0000000000 --- a/extra/postgresql/libpq/libpq.factor +++ /dev/null @@ -1,361 +0,0 @@ -! 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: postgresql.libpq - -<< -"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 ( ) ; - diff --git a/extra/postgresql/postgresql-tests.factor b/extra/postgresql/postgresql-tests.factor deleted file mode 100644 index c725882b67..0000000000 --- a/extra/postgresql/postgresql-tests.factor +++ /dev/null @@ -1,42 +0,0 @@ -! You will need to run 'createdb factor-test' to create the database. -! Set username and password in the 'connect' word. - -IN: postgresql-test -USING: kernel postgresql alien continuations io prettyprint -sequences namespaces ; - - -: test-connection ( host port pgopts pgtty db user pass -- bool ) - [ [ ] with-postgres ] catch "Error connecting!" "Connected!" ? print ; - -! just a basic demo - -"localhost" "" "" "" "test" "postgres" "" [ - "drop table animal" do-command - - "create table animal (id serial not null primary key, species varchar(256), name varchar(256), age integer)" do-command - "insert into animal (species, name, age) values ('lion', 'Mufasa', 5)" - do-command - - "select * from animal where name = 'Mufasa'" [ ] do-query - "select * from animal where name = 'Mufasa'" - [ - result>seq length 1 = [ "...there can only be one Mufasa..." throw ] unless - ] do-query - - "insert into animal (species, name, age) values ('lion', 'Simba', 1)" - do-command - - "select * from animal" - [ - "Animal table:" print - result>seq print-table - ] do-query - - ! intentional errors - ! [ "select asdf from animal" - ! [ ] do-query ] catch [ "caught: " write print ] when* - ! "select asdf from animal" [ ] do-query - ! "aofijweafew" do-command -] with-postgres - diff --git a/extra/postgresql/postgresql.factor b/extra/postgresql/postgresql.factor deleted file mode 100644 index 9d85b6a77e..0000000000 --- a/extra/postgresql/postgresql.factor +++ /dev/null @@ -1,61 +0,0 @@ -! 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 alien alien.syntax continuations io -kernel math namespaces postgresql.libpq prettyprint -quotations sequences debugger ; -IN: postgresql - -SYMBOL: db -SYMBOL: query-res - -: connect-postgres ( host port pgopts pgtty db user pass -- conn ) - PQsetdbLogin - dup PQstatus zero? [ "couldn't connect to database" throw ] unless ; - -: with-postgres ( host port pgopts pgtty db user pass quot -- ) - [ >r connect-postgres db set r> - [ db get PQfinish ] [ ] cleanup ] with-scope ; inline - -: postgres-error ( ret -- ret ) - dup zero? [ PQresultErrorMessage throw ] when ; - -: (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 PQresultStatus PGRES_COMMAND_OK = - over PQresultStatus PGRES_TUPLES_OK = - or [ - [ PQresultErrorMessage CHAR: \n swap remove ] keep 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 ; - -: 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 ; - From 161c3ec1560dbcc32f3006bac38b49a8a62a0338 Mon Sep 17 00:00:00 2001 From: Doug Coleman Date: Fri, 1 Feb 2008 17:45:34 -0600 Subject: [PATCH 04/16] remove sqlite and tupledb for now --- extra/sqlite/authors.txt | 1 - extra/sqlite/lib/authors.txt | 1 - extra/sqlite/lib/lib.factor | 120 --------- extra/sqlite/sqlite-docs.factor | 87 ------- extra/sqlite/sqlite-tests.factor | 69 ----- extra/sqlite/sqlite.factor | 127 --------- extra/sqlite/test.txt | 3 - extra/sqlite/tuple-db/authors.txt | 1 - extra/sqlite/tuple-db/tuple-db-docs.factor | 131 ---------- extra/sqlite/tuple-db/tuple-db-tests.factor | 39 --- extra/sqlite/tuple-db/tuple-db.factor | 270 -------------------- 11 files changed, 849 deletions(-) delete mode 100755 extra/sqlite/authors.txt delete mode 100755 extra/sqlite/lib/authors.txt delete mode 100644 extra/sqlite/lib/lib.factor delete mode 100644 extra/sqlite/sqlite-docs.factor delete mode 100644 extra/sqlite/sqlite-tests.factor delete mode 100644 extra/sqlite/sqlite.factor delete mode 100644 extra/sqlite/test.txt delete mode 100755 extra/sqlite/tuple-db/authors.txt delete mode 100644 extra/sqlite/tuple-db/tuple-db-docs.factor delete mode 100644 extra/sqlite/tuple-db/tuple-db-tests.factor delete mode 100644 extra/sqlite/tuple-db/tuple-db.factor diff --git a/extra/sqlite/authors.txt b/extra/sqlite/authors.txt deleted file mode 100755 index 44b06f94bc..0000000000 --- a/extra/sqlite/authors.txt +++ /dev/null @@ -1 +0,0 @@ -Chris Double diff --git a/extra/sqlite/lib/authors.txt b/extra/sqlite/lib/authors.txt deleted file mode 100755 index 44b06f94bc..0000000000 --- a/extra/sqlite/lib/authors.txt +++ /dev/null @@ -1 +0,0 @@ -Chris Double diff --git a/extra/sqlite/lib/lib.factor b/extra/sqlite/lib/lib.factor deleted file mode 100644 index 438f22a80f..0000000000 --- a/extra/sqlite/lib/lib.factor +++ /dev/null @@ -1,120 +0,0 @@ -! 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. -! Remeber to pass the following to factor: -! -libraries:sqlite=libsqlite3.so -! -! Not all functions have been wrapped yet. Only those directly involving -! executing SQL calls and obtaining results. -! -IN: sqlite.lib -USING: alien compiler kernel math namespaces sequences strings alien.syntax - system combinators ; - -<< -"sqlite" { - { [ win32? ] [ "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 - -TYPEDEF: void sqlite3 -TYPEDEF: void sqlite3_stmt - -LIBRARY: sqlite -FUNCTION: int sqlite3_open ( char* filename, void* ppDb ) ; -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 ) ; - diff --git a/extra/sqlite/sqlite-docs.factor b/extra/sqlite/sqlite-docs.factor deleted file mode 100644 index d58b553f11..0000000000 --- a/extra/sqlite/sqlite-docs.factor +++ /dev/null @@ -1,87 +0,0 @@ -! Copyright (C) 2006 Chris Double. -! See http://factorcode.org/license.txt for BSD license. -USING: help help.syntax help.markup ; -IN: sqlite - -HELP: sqlite-open -{ $values { "filename" "path to sqlite database" } - { "db" "the database object" } -} -{ $description "Opens the sqlite3 database." } -{ $see-also sqlite-close sqlite-last-insert-rowid } ; - -HELP: sqlite-close -{ $values { "db" "the database object" } -} -{ $description "Closes the sqlite3 database." } -{ $see-also sqlite-open sqlite-last-insert-rowid } ; - -HELP: sqlite-last-insert-rowid -{ $values { "db" "the database object" } - { "rowid" "the row number of the last insert" } -} -{ $description "Returns the number of the row of the last statement inserted into the database." } -{ $see-also sqlite-open sqlite-close } ; - -HELP: sqlite-prepare -{ $values { "db" "the database object" } - { "sql" "the SQL statement as a string" } - { "statement" "the prepared SQL statement" } -} -{ $description "Internally compiles the SQL statement ready to be run by sqlite. The statement is executed and the results iterated over using " { $link sqlite-each } " and " { $link sqlite-map } ". The SQL statement can use named parameters which are later bound to values using " { $link sqlite-bind-text } " and " { $link sqlite-bind-text-by-name } "." } -{ $see-also sqlite-open sqlite-close } ; - -HELP: sqlite-bind-text -{ $values { "statement" "a prepared SQL statement" } - { "index" "the index of the bound parameter in the SQL statement" } - { "text" "the string value to bind to that column" } - -} -{ $description "Binds the text to a parameter in the SQL statement. The parameter to be bound is identified by the index given and the indexes start from one." } -{ $examples { $code "\"people.db\" sqlite-open\n\"select * from people where name=?\" sqlite-prepare\n1 \"chris\" sqlite-bind-text" } } -{ $see-also sqlite-bind-text-by-name } ; - -HELP: sqlite-bind-text-by-name -{ $values { "statement" "a prepared SQL statement" } - { "name" "the name of the bound parameter in the SQL statement" } - { "text" "the string value to bind to that column" } - -} -{ $description "Binds the text to a parameter in the SQL statement. The parameter to be bound is identified by the given name." } -{ $examples { $code "\"people.db\" sqlite-open\n\"select * from people where name=:name\" sqlite-prepare\n\"name\" \"chris\" sqlite-bind-text" } } -{ $see-also sqlite-bind-text } ; - -HELP: sqlite-finalize -{ $values { "statement" "a prepared SQL statement" } -} -{ $description "Clean up all resources related to a statement. Once called the statement cannot be used again. All statements must be finalized before closing the database." } -{ $see-also sqlite-close sqlite-prepare } ; - -HELP: sqlite-reset -{ $values { "statement" "a prepared SQL statement" } -} -{ $description "Reset a statement so it can be called again, possibly with different bound parameters." } -{ $see-also sqlite-bind-text sqlite-bind-text-by-name } ; - -HELP: column-count -{ $values { "statement" "a prepared SQL statement" } { "int" "the number of columns" } } -{ $description "Return the number of columns in each row of the result set of the given statement." } -{ $see-also column-text sqlite-each sqlite-map } ; - -HELP: column-text -{ $values { "statement" "a prepared SQL statement" } { "index" "column number indexed from zero" } { "string" "column value" } -} -{ $description "Return the value of the given column, indexed from zero, as a string." } -{ $see-also column-count sqlite-each sqlite-map } ; - -HELP: sqlite-each -{ $values { "statement" "a prepared SQL statement" } { "quot" "A quotation with stack effect ( statement -- )" } -} -{ $description "Executes the SQL statement and for each returned row calls the qutotation passing the statement on the stack. The quotation can use " { $link column-text } " to get result values for that row." } -{ $see-also column-count column-text sqlite-map } ; - -HELP: sqlite-map -{ $values { "statement" "a prepared SQL statement" } { "quot" "A quotation with stack effect ( statement -- value )" } { "seq" "a new sequence" } -} -{ $description "Executes the SQL statement and for each returned row calls the qutotation passing the statement on the stack. The quotation can use " { $link column-text } " to get result values for that row. The quotation should leave a value on the stack which gets collected and returned in the resulting sequence." } -{ $see-also column-count column-text sqlite-each } ; diff --git a/extra/sqlite/sqlite-tests.factor b/extra/sqlite/sqlite-tests.factor deleted file mode 100644 index 5eecbec369..0000000000 --- a/extra/sqlite/sqlite-tests.factor +++ /dev/null @@ -1,69 +0,0 @@ -! Copyright (C) 2005 Chris Double. -! -! Redistribution and use in source and binary forms, with or without -! modification, are permitted provided that the following conditions are met: -! -! 1. Redistributions of source code must retain the above copyright notice, -! this list of conditions and the following disclaimer. -! -! 2. Redistributions in binary form must reproduce the above copyright notice, -! this list of conditions and the following disclaimer in the documentation -! and/or other materials provided with the distribution. -! -! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, -! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -! -! Test the sqlite interface -! -! Create a test database like follows: -! -! sqlite3 test.db < test.txt -! -! Then run this file. -USE: sqlite -USE: kernel -USE: io -USE: io.files -USE: prettyprint - -: test.db "libs/sqlite/test.db" resource-path ; - -: show-people ( statement -- ) - dup 0 column-text write " from " write 1 column-text . ; - -: run-test ( -- ) - test.db sqlite-open - dup "select * from test" sqlite-prepare - dup [ show-people ] sqlite-each - sqlite-finalize - sqlite-close ; - -: find-person ( name -- ) - test.db sqlite-open ! name db - dup "select * from test where name=?" sqlite-prepare ! name db stmt - [ rot 1 swap sqlite-bind-text ] keep ! db stmt - [ [ 1 column-text . ] sqlite-each ] keep - sqlite-finalize - sqlite-close ; - -: find-all ( -- ) - test.db sqlite-open ! db - dup "select * from test" sqlite-prepare ! db stmt - [ [ [ 0 column-text ] keep 1 column-text curry ] sqlite-map ] keep - sqlite-finalize - swap sqlite-close ; - -: run-test2 ( -- ) - test.db sqlite-open - dup "select * from test" sqlite-prepare - dup [ show-people ] ; - -run-test diff --git a/extra/sqlite/sqlite.factor b/extra/sqlite/sqlite.factor deleted file mode 100644 index d651ad916c..0000000000 --- a/extra/sqlite/sqlite.factor +++ /dev/null @@ -1,127 +0,0 @@ -! 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*" [ 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*" "void*" - [ 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 ; - diff --git a/extra/sqlite/test.txt b/extra/sqlite/test.txt deleted file mode 100644 index 5c7ae2b52a..0000000000 --- a/extra/sqlite/test.txt +++ /dev/null @@ -1,3 +0,0 @@ -create table test (name varchar(30), address varchar(30)); -insert into test values('John', 'America'); -insert into test values('Jane', 'New Zealand'); diff --git a/extra/sqlite/tuple-db/authors.txt b/extra/sqlite/tuple-db/authors.txt deleted file mode 100755 index 44b06f94bc..0000000000 --- a/extra/sqlite/tuple-db/authors.txt +++ /dev/null @@ -1 +0,0 @@ -Chris Double diff --git a/extra/sqlite/tuple-db/tuple-db-docs.factor b/extra/sqlite/tuple-db/tuple-db-docs.factor deleted file mode 100644 index 3c6df0eaa6..0000000000 --- a/extra/sqlite/tuple-db/tuple-db-docs.factor +++ /dev/null @@ -1,131 +0,0 @@ -! Copyright (C) 2006 Chris Double. -! See http://factorcode.org/license.txt for BSD license. -USING: help sqlite help.syntax help.markup ; -IN: sqlite.tuple-db - -ARTICLE: { "sqlite" "tuple-db-loading" } "Loading" -"The quickest way to get up and running with this library is to use the vocabulary:" -{ $code "USING: sqlite sqlite.tuple-db ;\n" } -"Some simple tests can be run to check that everything is working ok:" -{ $code "\"libs/sqlite\" test-module" } ; - -ARTICLE: { "sqlite" "tuple-db-usage" } "Basic Usage" -"This library can be used for storing simple Factor tuples in a sqlite database. In its current form the tuples must not contain references to other tuples and should not have a delegate set." -$nl -"This document will use the following tuple for demonstration purposes:" -{ $code "TUPLE: person name surname phone ;" } -"The sqlite database to store tuples must be created, or an existing one opened. This is done using the " { $link sqlite-open } " word. If the database does not exist then it is created. The examples in this document store the database pointer in a variable called 'db':" -{ $code "SYMBOL: db\n\"example.db\" sqlite-open db set-global" } ; - -ARTICLE: { "sqlite" "tuple-db-mappings" } "Tuple Mappings" -"Each tuple has a 'mapping' tuple associated with it. The 'mapping' stores information about what table the tuple will be stored in, the datatypes of the tuple slots, etc. A mapping must be created before a tuple can be stored in a database. A default mapping is easily created using " { $link default-mapping } ". Given the tuple class, this will use reflection to get the slots of it, assume that all slots are of database type 'text', and store the tuple objects in a table with the same name as the tuple." -$nl -"The following shows how to create the default mapping for the 'person' tuple, and how to register that mapping so the 'tuple-db' system can know how to handle 'person' instances:" -{ $code "person default-mapping set-mapping" } ; - -ARTICLE: { "sqlite" "tuple-db-create" } "Creating the table" -"The table used to store tuple instances may need to be created. This can be done manually using the external sqlite program or via " { $link create-tuple-table } ":" -{ $code "db get person create-tuple-table" } -"The SQL used to create the table is produced internally by " { $link create-sql } ". This is a generic word dispatched on the mapping object, and could be specialised if needed. If you wish to see the SQL used to create the table, use the following code:" -{ $code "person get-mapping create-sql .\n => \"create table person (name text,surname text,phone text);\"" } ; - -ARTICLE: { "sqlite" "tuple-db-insert" } "Inserting instances" -"The " { $link insert-tuple } " word will store instances of a tuple into the database table defined by its mapping object:" -{ $code "db get \"John\" \"Smith\" \"123-456-789\" insert-tuple" } -{ $link insert-tuple } " internally uses the " { $link insert-sql } " word to produce the SQL used to store the tuple. Like " { $link create-sql } ", it is a generic word specialized on the mapping object. You can call it directly to see what SQL is generated:" -{ $code "person get-mapping insert-sql .\n => \"insert into person values(:name,:surname,:phone);\"" } -"Notice that the SQL uses named parameters. These parameters are bound to the values stored in the tuple object when the SQL is compiled. This helps prevent SQL injection techniques." -$nl -"When " { $link insert-sql } " is run, it adds a delegate to the tuple being stored. The delegate is of type 'persistent' and holds the row id of the tuple in its 'key' slot. This way the exact record can be updated or retrieved later. The following demonstates this fact:" -{ $code "\"Mandy\" \"Jones\" \"987-654-321\" dup .\n => T{ person f \"Mandy\" \"Jones\" \"987-654-321\" }\ndb get over insert-tuple .\n => T{ person T{ persistent ... 2 } \"Mandy\" \"Jones\" \"987-654-321\" }" } -"The '2' in the above example is the row id of the record inserted. We can go into the 'sqlite' command and view this record:" -{ $code " $ sqlite3 example.db\n SQLite version 3.0.8\n Enter \".help\" for instructions\n sqlite> select ROWID,* from person;\n 1|John|Smith|123-456-789\n 2|Mandy|Jones|987-654-321\n sqlite>" } ; - -ARTICLE: { "sqlite" "tuple-db-finding" } "Finding instances" -"The " { $link find-tuples } " word is used to return tuples populated with data already existing in the database. As well as the database objcet, it takes a tuple that should be populated only with the fields that should be matched in the database. All fields you do not wish to match against should be set to 'f':" -{ $code "db get f \"Smith\" f find-tuples .\n => { T{ person # \"John\" \"Smith\" \"123-456-789\" } }\ndb get \"Mandy\" f f find-tuples .\n => { T{ person # \"Mandy\" \"Jones\" \"987-654-321\" } }\ndb get \"Joe\" f f find-tuples .\n => { }" } -"Notice that if no matching tuples are found then an empty sequence is returned. The returned tuples also have their delegate set to 'persistent' with the correct row id set as the key. This can be used to later update the tuples with new information and store them in the database." ; - -ARTICLE: { "sqlite" "tuple-db-updating" } "Updating instances" -"Given a tuple that has the 'persistent' delegate with the row id set as the key, you can update this specific record using " { $link update-tuple } ":" -{ $code "db get f \"Smith\" f find-tuples dup .\n => { T{ person # \"John\" \"Smith\" \"123-456-789\" } }\nfirst { \"999-999-999\" swap set-person-phone ] keep dup .\n => T{ person T{ persistent f # \"1\" } \"John\" \"Smith\" \"999-999-999\" ...\n db get swap update-tuple" } -"Using the 'sqlite' command from the system shell you can see the record was updated:" -{ $code " $ sqlite3 example.db\n SQLite version 3.0.8\n Enter \".help\" for instructions\n sqlite> select ROWID,* from person;\n 1|John|Smith|999-999-999\n 2|Mandy|Jones|987-654-321\n sqlite>" } ; - -ARTICLE: { "sqlite" "tuple-db-inserting-or-updating" } "Inserting or Updating instances" -"The " { $link save-tuple } " word can be used to insert a tuple if it has not already been stored in the database, or update it if it already exists. Whether to insert or update is decided by the existance of the 'persistent' delegate:" -{ $code "\"Mary\" \"Smith\" \"111-111-111\" dup .\n => T{ person f \"Mary\" \"Smith\" \"111-111-111\" }\n! This will insert the tuple\ndb get over save-tuple dup .\n => T{ person T{ persistent f # \"3\" } \"Mary\" \"Smith\" \"111-111-111\" ...\n[ \"222-222-222\" swap set-person-phone ] keep dup .\n => T{ person T{ persistent f # \"3\" } \"Mary\" \"Smith\" \"222-222-222\" ...\n! This will update the tuple\ndb get over save-tuple .\n => T{ person T{ persistent f # \"3\" } \"Mary\" \"Smith\" \"222-222-222\" ..." } ; - -ARTICLE: { "sqlite" "tuple-db-deleting" } "Deleting instances" -"Given a tuple with the delegate set to 'persistent' (ie. One already stored in the database) you can delete it from the database with " { $link delete-tuple } ":" -{ $code "db get f \"Smith\" f find-tuples [ db get swap delete-tuple ] each" } ; - -ARTICLE: { "sqlite" "tuple-db-closing" } "Closing the database" -"It's important to close the sqlite database when you've finished using it. The word for this is " { $link sqlite-close } ":" -{ $code "db get sqlite-close" } ; - -ARTICLE: { "sqlite" "tuple-db" } "Tuple Database Library" -"The version of sqlite required by this library is version 3 or greater. This library allows storing Factor tuples in a sqlite database. It provides words to create, read update and delete these entries as well as simple searching." -$nl -"The library is in a very early state and is likely to change quite a bit in the near future. Its most notable omission is it cannot currently handle relationships between tuples." -{ $subsection { "sqlite" "tuple-db-loading" } } -{ $subsection { "sqlite" "tuple-db-usage" } } -{ $subsection { "sqlite" "tuple-db-mappings" } } -{ $subsection { "sqlite" "tuple-db-create" } } -{ $subsection { "sqlite" "tuple-db-insert" } } -{ $subsection { "sqlite" "tuple-db-finding" } } -{ $subsection { "sqlite" "tuple-db-updating" } } -{ $subsection { "sqlite" "tuple-db-inserting-or-updating" } } -{ $subsection { "sqlite" "tuple-db-deleting" } } -{ $subsection { "sqlite" "tuple-db-closing" } } -; - -HELP: default-mapping -{ $values { "class" "symbol for the tuple class" } - { "mapping" "a mapping object" } -} -{ $description "Given a tuple class, create a default mappings object. This is used to associate field names in the tuple with SQL statement field names, etc." } -{ $see-also { "sqlite" "tuple-db" } set-mapping } ; - -HELP: set-mapping -{ $values { "mapping" "a mapping object" } -} -{ $description "Store a database mapping so that the tuple-db system knows how to store instances of the tuple in the database." } -{ $see-also { "sqlite" "tuple-db" } default-mapping } ; - -HELP: create-tuple-table -{ $values { "db" "a database object" } { "class" "symbol for the tuple class" } -} -{ $description "Create the database table to store intances of the given tuple." } -{ $see-also { "sqlite" "tuple-db" } default-mapping get-mapping } ; - -HELP: insert-tuple -{ $values { "db" "a database object" } { "tuple" "an instance of a tuple" } -} -{ $description "Insert the tuple instance into the database. It is assumed that this tuple does not currently exist in the database." } -{ $see-also { "sqlite" "tuple-db" } insert-tuple update-tuple find-tuples delete-tuple save-tuple } ; - -HELP: find-tuples -{ $values { "db" "a database object" } { "tuple" "an instance of a tuple" } { "seq" "a sequence of tuples" } } -{ $description "Return a sequence of all tuples in the database that match the tuple provided as a template. All fields in the tuple must match the entries in the database, except for those set to 'f'." } -{ $see-also { "sqlite" "tuple-db" } insert-tuple update-tuple find-tuples delete-tuple save-tuple } ; - -HELP: update-tuple -{ $values { "db" "a database object" } { "tuple" "an instance of a tuple" } -} -{ $description "Update the database record for this tuple instance. The tuple must have previously been obtained from the database, or inserted into it. It must have a delegate of 'persistent' with the key field set (which is done by the find and insert operations)." } -{ $see-also { "sqlite" "tuple-db" } insert-tuple update-tuple find-tuples delete-tuple save-tuple } ; - -HELP: save-tuple -{ $values { "db" "a database object" } { "tuple" "an instance of a tuple" } -} -{ $description "Insert or Update the tuple instance depending on whether it has a persistent delegate." } -{ $see-also { "sqlite" "tuple-db" } insert-tuple update-tuple find-tuples delete-tuple save-tuple } ; - -HELP: delete-tuple -{ $values { "db" "a database object" } { "tuple" "an instance of a tuple" } -} -{ $description "Delete this tuple instance from the database. The tuple must have previously been obtained from the database, or inserted into it. It must have a delegate of 'persistent' with the key field set (which is done by the find and insert operations)." } -{ $see-also { "sqlite" "tuple-db" } insert-tuple update-tuple find-tuples delete-tuple save-tuple } ; - -ABOUT: { "sqlite" "tuple-db" } \ No newline at end of file diff --git a/extra/sqlite/tuple-db/tuple-db-tests.factor b/extra/sqlite/tuple-db/tuple-db-tests.factor deleted file mode 100644 index 8ed2631b45..0000000000 --- a/extra/sqlite/tuple-db/tuple-db-tests.factor +++ /dev/null @@ -1,39 +0,0 @@ -! Copyright (C) 2005 Chris Double. -! See http://factorcode.org/license.txt for BSD license. - -IN: temporary -USING: io io.files kernel sequences namespaces -hashtables sqlite sqlite.tuple-db math words tools.test ; - -TUPLE: testdata one two ; - -C: testdata - -testdata default-mapping set-mapping - -"libs/sqlite/test.db" resource-path [ - - db get testdata create-tuple-table - - [ "two" { } ] [ - db get "one" "two" insert-tuple - db get "one" f find-tuples - first [ testdata-two ] keep - db get swap delete-tuple - db get "one" f find-tuples - ] unit-test - - [ "junk" ] [ - db get "one" "two" insert-tuple - db get "one" f find-tuples - first - "junk" over set-testdata-two - db get swap update-tuple - db get "one" f find-tuples - first [ testdata-two ] keep - db get swap delete-tuple - ] unit-test - - db get testdata drop-tuple-table -] with-sqlite - diff --git a/extra/sqlite/tuple-db/tuple-db.factor b/extra/sqlite/tuple-db/tuple-db.factor deleted file mode 100644 index c37a49d2b6..0000000000 --- a/extra/sqlite/tuple-db/tuple-db.factor +++ /dev/null @@ -1,270 +0,0 @@ -! Copyright (C) 2005 Chris Double. -! -! A tuple that is persistent has its delegate set as 'persistent'. -! 'persistent' holds the numeric rowid for that tuple in its table. -IN: sqlite.tuple-db -USING: io kernel sequences namespaces slots classes slots.private -assocs math words generic sqlite math.parser ; - -! Each slot in a tuple that is storable in the database has -! an instance of a db-field object the gives the name of the -! database table and slot number in the tuple object of that field. -TUPLE: db-field name bind-name slot type ; - -C: db-field - -! The mapping tuple holds information on how the slots of -! a tuple are mapped to the fields of a sqlite database. -TUPLE: mapping tuple table fields one-to-one one-to-many ; - -C: mapping - -: sanitize ( string -- string ) - #! Convert a string so it can be used as a table or field name. - clone - H{ { CHAR: - CHAR: _ } { CHAR: ? CHAR: p } } - over substitute ; - -: tuple-fields ( class -- seq ) - #! Given a tuple class return a list of the fields - #! within that tuple. Ignores the delegate field. - "slots" word-prop 1 tail [ - [ slot-spec-name sanitize dup ":" swap append ] keep - slot-spec-offset - "text" - - ] map ; - -: default-mapping ( class -- mapping ) - #! Given a tuple class, create a default mappings object. It assumes - #! there are no one-to-one or one-to-many relationships. - dup [ word-name sanitize ] keep tuple-fields f f ; - -! The mappings variable holds a hashtable mapping the tuple symbol -! to the mapping object, describing how that tuple is stored -! in the database. -SYMBOL: mappings - -: init-mappings ( -- ) - H{ } mappings set-global ; - -: get-mappings ( -- hashtable ) - mappings get-global ; - -: set-mapping ( mapping -- ) - #! Store a database mapping so that the persistence system - #! knows how to store instances of the relevant tuple in the database. - dup mapping-tuple get-mappings set-at ; - -: get-mapping ( class -- mapping ) - #! Return the database mapping for the given tuple class. - get-mappings at ; - -! The 'persistent' tuple will be set to the delegate of any tuple -! instance stored in the database. It contains the database key -! of the row in the database table for the instance or 'f' if it has -! not yet been stored in the database. It also contains the 'mapping' -! object used to translate the fields of the tuple to the database fields. -TUPLE: persistent mapping key ; -: ( tuple -- persistent ) - persistent construct-empty - >r class get-mapping r> - [ set-persistent-mapping ] keep ; - -: make-persistent ( tuple -- tuple ) - #! Convert the tuple into something that can be stored - #! into a database by setting its delegate to 'persistent'. - [ ] keep - [ set-delegate ] keep ; - - -: comma-fields ( mapping quot -- string ) - #! Given a mapping, call quot on each field in - #! the mapping. The contents of quot should call ',' or '%' - #! to generate output. The output of each quot call - #! seperated by commas is returned as a string. 'quot' should be - #! stack effect ( field -- ). - >r mapping-fields r> [ "" make ] curry map "," join ; inline - -GENERIC: create-sql ( mapping -- string ) -M: mapping create-sql ( mapping -- string ) - #! Return the SQL used to create a table for storing this type of tuple. - [ - "create table " % dup mapping-table % - " (" % - [ dup db-field-name % " " % db-field-type % ] comma-fields % - ");" % - ] "" make ; - -GENERIC: drop-sql ( mapping -- string ) -M: mapping drop-sql ( mapping -- string ) - #! Return the SQL used to drop the table for storing this type of tuple. - [ - "drop table " % mapping-table % ";" % - ] "" make ; - -GENERIC: insert-sql ( mapping -- string ) -M: mapping insert-sql ( mapping -- string ) - #! Return the SQL used to insert a tuple into a table - [ - "insert into " % dup mapping-table % - " values(" % - [ db-field-bind-name % ] comma-fields % - ");" % - ] "" make ; - -GENERIC: delete-sql ( mapping -- string ) -M: mapping delete-sql ( mapping -- string ) - #! Return the SQL used to delete a tuple from a table - [ - "delete from " % mapping-table % - " where ROWID=:rowid;" % - ] "" make ; - -GENERIC: update-sql ( mapping -- string ) -M: mapping update-sql ( mapping -- string ) - #! Return the SQL used to update the tuple - [ - "update " % dup mapping-table % - " set " % - [ dup db-field-name % "=" % db-field-bind-name % ] comma-fields % - " where ROWID=:rowid;" % - ] "" make ; - -GENERIC: select-sql ( tuple mapping -- select ) -M: mapping select-sql ( tuple mapping -- select ) - #! Return the SQL used to select a series of tuples from the database. It - #! will select based on only the filled in fields of the tuple (ie. all non-f). - [ - "select ROWID,* from " % dup mapping-table % - mapping-fields [ ! tuple field - swap over db-field-slot slot ! field value - [ - [ dup db-field-name % "=" % db-field-bind-name % ] "" make - ] [ - drop f - ] if - ] with map [ ] subset dup length 0 > [ - " where " % - " and " join % - ] [ - drop - ] if - ";" % - ] "" make ; - -: execute-update-sql ( db string -- ) - #! Execute the SQL, which should contain a database update - #! statement (update, insert, create, etc). Ignore the result. - sqlite-prepare dup [ drop ] sqlite-each sqlite-finalize ; - -: create-tuple-table ( db class -- ) - #! Create the table for the tuple class. - get-mapping create-sql execute-update-sql ; - -: drop-tuple-table ( db class -- ) - #! Create the table for the tuple class. - get-mapping drop-sql execute-update-sql ; - -: bind-for-insert ( statement tuple -- ) - #! Bind the fields in the tuple to the fields in the - #! prepared insert statement. - dup class get-mapping mapping-fields [ ! statement tuple field - [ db-field-slot slot ] keep ! statement value field - db-field-bind-name swap ! statement name value - >r dupd r> sqlite-bind-text-by-name - ] with each drop ; - -: bind-for-select ( statement tuple -- ) - #! Bind the fields in the tuple to the fields in the - #! prepared select statement. - dup class get-mapping mapping-fields [ ! statement tuple field - [ db-field-slot slot ] keep ! statement value field - over [ - db-field-bind-name swap ! statement name value - >r dupd r> sqlite-bind-text-by-name - ] [ - 2drop - ] if - ] with each drop ; - -: bind-for-update ( statement tuple -- ) - #! Bind the fields in the tuple to the fields in the - #! prepared update statement. - 2dup bind-for-insert - >r ":rowid" r> persistent-key sqlite-bind-text-by-name ; - -: bind-for-delete ( statement tuple -- ) - #! Bind the fields in the tuple to the fields in the - #! prepared delete statement. - >r ":rowid" r> persistent-key sqlite-bind-text-by-name ; - -: (insert-tuple) ( db tuple -- ) - #! Insert this tuple instance into the database. Note that - #! it inserts only this instance, and not any one-to-one or - #! one-to-many fields. - dup class get-mapping insert-sql ! db tuple sql - swapd sqlite-prepare swap ! statement tuple - dupd bind-for-insert ! statement - dup [ drop ] sqlite-each - sqlite-finalize ; - -: insert-tuple ( db tuple -- ) - #! Insert this tuple instance into the database and - #! update the rowid of the insert in the tuple. - [ (insert-tuple) ] 2keep - >r sqlite-last-insert-rowid number>string r> make-persistent set-persistent-key ; - -: update-tuple ( db tuple -- ) - #! Update this tuple instance in the database. The tuple should have - #! a delegate of 'persistent' with the key field set. - dup class get-mapping update-sql ! db tuple sql - swapd sqlite-prepare swap ! statement tuple - dupd bind-for-update ! statement - dup [ drop ] sqlite-each - sqlite-finalize ; - -: save-tuple ( db tuple -- ) - #! Insert or Update the tuple instance depending on whether it - #! has a persistent delegate. - dup delegate [ update-tuple ] [ insert-tuple ] if ; - -: delete-tuple ( db tuple -- ) - #! Delete this tuple instance from the database. The tuple should have - #! a delegate of 'persistent' with the key field set. - dup class get-mapping delete-sql ! db tuple sql - swapd sqlite-prepare swap ! statement tuple - dupd bind-for-delete ! statement - dup [ drop ] sqlite-each - sqlite-finalize ; - -: restore-tuple ( statement tuple -- tuple ) - #! Using 'tuple' as a template, clone it and - #! return the clone with fields set to the values from the - #! database. - clone dup class get-mapping mapping-fields 1 swap - [ ! statement tuple index field ) - over 1+ >r ! statement tuple index field r: index+1 - db-field-slot >r ! statement tuple index r: index+1 slot - pick swap column-text ! statement tuple value r: index+1 slot - over r> set-slot r> ! statement tuple index+1 - ] each ! statement tuple index - drop make-persistent swap 0 column-text swap [ set-persistent-key ] keep ; - -: find-tuples ( db tuple -- seq ) - #! Return a sequence of all tuples in the database that - #! match the tuple provided as a template. All fields in the - #! tuple must match the entries in the database, except for - #! those set to 'f'. - dup class get-mapping dupd select-sql ! db tuple sql - swapd sqlite-prepare swap ! statement tuple - 2dup bind-for-select ! statement tuple - [ - over [ ! tuple statement - over restore-tuple , - ] sqlite-each - ] { } make nip ! statement tuple accum - swap sqlite-finalize ; - - -get-mappings [ init-mappings ] unless From db3ac4d75ff5835d49bd9821cd303d724f330a6d Mon Sep 17 00:00:00 2001 From: Doug Coleman Date: Fri, 1 Feb 2008 22:46:03 -0600 Subject: [PATCH 05/16] intermediate work on cookies --- extra/http/http.factor | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/extra/http/http.factor b/extra/http/http.factor index 9e5d34fa36..a71e003433 100755 --- a/extra/http/http.factor +++ b/extra/http/http.factor @@ -1,18 +1,18 @@ ! Copyright (C) 2003, 2007 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. USING: hashtables io kernel math namespaces math.parser assocs -sequences strings splitting ; +sequences strings splitting assocs.lib ; IN: http : header-line ( line -- ) - ": " split1 dup [ swap set ] [ 2drop ] if ; + ": " split1 dup [ swap >lower set ] [ 2drop ] if ; : (read-header) ( -- ) readln dup empty? [ drop ] [ header-line (read-header) ] if ; : read-header ( -- hash ) - [ (read-header) ] H{ } make-assoc ; + [ (read-header) ] VH{ } make-assoc ; : url-quotable? ( ch -- ? ) #! In a URL, can this character be used without @@ -74,4 +74,3 @@ IN: http hash>query % ] if ] "" make ; - From 004dd0dc5e97f60bf917b2af99673c3fa2bbe754 Mon Sep 17 00:00:00 2001 From: Doug Coleman Date: Fri, 1 Feb 2008 22:46:32 -0600 Subject: [PATCH 06/16] add accumulator --- extra/sequences/lib/lib.factor | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extra/sequences/lib/lib.factor b/extra/sequences/lib/lib.factor index e46ce3b107..9aac0a50bd 100755 --- a/extra/sequences/lib/lib.factor +++ b/extra/sequences/lib/lib.factor @@ -140,3 +140,6 @@ PRIVATE> : ?second ( seq -- second/f ) 1 swap ?nth ; inline : ?third ( seq -- third/f ) 2 swap ?nth ; inline : ?fourth ( seq -- fourth/f ) 3 swap ?nth ; inline + +: accumulator ( quot -- quot vec ) + V{ } clone [ [ push ] curry compose ] keep ; From 9e9c71b6d0925d5929bbb10a20807fd3d75cfb6c Mon Sep 17 00:00:00 2001 From: Doug Coleman Date: Fri, 1 Feb 2008 23:46:44 -0600 Subject: [PATCH 07/16] make multi-assocs work for http headers --- extra/http/client/client.factor | 4 +-- extra/http/http.factor | 7 +++-- .../http/server/responders/responders.factor | 28 +++++++++++-------- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/extra/http/client/client.factor b/extra/http/client/client.factor index 7eb84fba4c..8e6d8257a4 100755 --- a/extra/http/client/client.factor +++ b/extra/http/client/client.factor @@ -2,7 +2,7 @@ ! See http://factorcode.org/license.txt for BSD license. USING: assocs http kernel math math.parser namespaces sequences io io.sockets io.streams.string io.files strings splitting -continuations ; +continuations assocs.lib ; IN: http.client : parse-host ( url -- host port ) @@ -44,7 +44,7 @@ DEFER: http-get-stream #! Should this support Location: headers that are #! relative URLs? pick 100 /i 3 = [ - dispose "location" swap header-single nip http-get-stream + dispose "location" swap peek-at nip http-get-stream ] when ; : http-get-stream ( url -- code headers stream ) diff --git a/extra/http/http.factor b/extra/http/http.factor index 4999559324..755f36a538 100755 --- a/extra/http/http.factor +++ b/extra/http/http.factor @@ -1,18 +1,19 @@ ! Copyright (C) 2003, 2007 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. USING: hashtables io kernel math namespaces math.parser assocs -sequences strings splitting ascii io.utf8 assocs.lib ; +sequences strings splitting ascii io.utf8 assocs.lib +namespaces unicode.case ; IN: http : header-line ( line -- ) - ": " split1 dup [ swap >lower set ] [ 2drop ] if ; + ": " split1 dup [ swap >lower insert ] [ 2drop ] if ; : (read-header) ( -- ) readln dup empty? [ drop ] [ header-line (read-header) ] if ; : read-header ( -- hash ) - [ (read-header) ] VH{ } make-assoc ; + [ (read-header) ] H{ } make-assoc ; : url-quotable? ( ch -- ? ) #! In a URL, can this character be used without diff --git a/extra/http/server/responders/responders.factor b/extra/http/server/responders/responders.factor index 8dcaa7223d..a507a95a14 100644 --- a/extra/http/server/responders/responders.factor +++ b/extra/http/server/responders/responders.factor @@ -2,7 +2,7 @@ ! See http://factorcode.org/license.txt for BSD license. USING: arrays assocs hashtables html html.elements splitting http io kernel math math.parser namespaces parser sequences -strings io.server ; +strings io.server vectors vector-hash strings.lib ; IN: http.server.responders @@ -10,8 +10,11 @@ IN: http.server.responders SYMBOL: vhosts SYMBOL: responders +: >header ( value key -- vector-hash ) + VH{ } clone [ set-at ] keep ; + : print-header ( alist -- ) - [ swap write ": " write print ] assoc-each nl ; + [ swap >Upper-dashes write ": " write print ] vector-hash-each nl ; : response ( msg -- ) "HTTP/1.0 " write print ; @@ -20,7 +23,7 @@ SYMBOL: responders : error-head ( error -- ) dup log-error response - H{ { "Content-Type" "text/html" } } print-header nl ; + VH{ { "Content-Type" "text/html" } } print-header nl ; : httpd-error ( error -- ) #! This must be run from handle-request @@ -36,7 +39,7 @@ SYMBOL: responders : serving-content ( mime -- ) "200 Document follows" response - "Content-Type" associate print-header ; + "Content-Type" >header print-header ; : serving-html "text/html" serving-content ; @@ -46,7 +49,7 @@ SYMBOL: responders : serving-text "text/plain" serving-content ; : redirect ( to response -- ) - response "Location" associate print-header ; + response "Location" >header print-header ; : permanent-redirect ( to -- ) "301 Moved Permanently" redirect ; @@ -84,14 +87,14 @@ SYMBOL: max-post-request : log-headers ( hash -- ) [ drop { - "User-Agent" - "Referer" - "X-Forwarded-For" - "Host" + "user-agent" + "referer" + "x-forwarded-for" + "host" } member? ] assoc-subset [ ": " swap 3append log-message - ] assoc-each ; + ] vector-hash-each ; : prepare-url ( url -- url ) #! This is executed in the with-request namespace. @@ -122,7 +125,8 @@ SYMBOL: max-post-request : query-param ( key -- value ) "query" get at ; -: header-param ( key -- value ) "header" get at ; +: header-param ( key -- value ) + "header" get peek-at ; : host ( -- string ) #! The host the current responder was called from. @@ -130,7 +134,7 @@ SYMBOL: max-post-request : add-responder ( responder -- ) #! Add a responder object to the list. - "responder" over at responders get set-at ; + "responder" over at responders get set-at ; : make-responder ( quot -- ) #! quot has stack effect ( url -- ) From 22eb97778e04197530130d280959832db727111d Mon Sep 17 00:00:00 2001 From: Doug Coleman Date: Fri, 1 Feb 2008 23:47:37 -0600 Subject: [PATCH 08/16] add multi-assocs --- extra/assocs/lib/lib.factor | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/extra/assocs/lib/lib.factor b/extra/assocs/lib/lib.factor index 849f88023f..182f04a367 100755 --- a/extra/assocs/lib/lib.factor +++ b/extra/assocs/lib/lib.factor @@ -1,9 +1,6 @@ -USING: assocs kernel vectors sequences ; +USING: assocs kernel vectors sequences namespaces ; IN: assocs.lib -: insert-at ( value key assoc -- ) - [ ?push ] change-at ; - : >set ( seq -- hash ) [ dup ] H{ } map>assoc ; @@ -19,5 +16,19 @@ IN: assocs.lib : at-default ( key assoc -- value/key ) dupd at [ nip ] when* ; -: at-peek ( key assoc -- value ? ) - at* dup >r [ peek ] when r> ; +: insert-at ( value key assoc -- ) + [ ?push ] change-at ; + +: peek-at* ( key assoc -- obj ? ) + at* dup [ >r peek r> ] when ; + +: peek-at ( key assoc -- obj ) + peek-at* drop ; + +: >multi-assoc ( assoc -- new-assoc ) + [ 1vector ] assoc-map ; + +: multi-assoc-each ( assoc quot -- ) + [ with each ] curry assoc-each ; inline + +: insert ( value variable -- ) namespace insert-at ; From 698f4180bbd580c73f1cf3204cc83626a6245a7b Mon Sep 17 00:00:00 2001 From: Doug Coleman Date: Fri, 1 Feb 2008 23:47:54 -0600 Subject: [PATCH 09/16] add a wget-bootstrap option --- misc/factor.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/misc/factor.sh b/misc/factor.sh index 39a15f93dc..032b0b3184 100755 --- a/misc/factor.sh +++ b/misc/factor.sh @@ -289,7 +289,7 @@ install_libraries() { } usage() { - echo "usage: $0 install|install-x11|self-update|quick-update|update|bootstrap" + echo "usage: $0 install|install-x11|self-update|quick-update|update|bootstrap|wget-bootstrap" } case "$1" in @@ -299,5 +299,6 @@ case "$1" in quick-update) update; refresh_image ;; update) update; update_bootstrap ;; bootstrap) get_config_info; bootstrap ;; + wget-bootstrap) get_config_info; delete_boot_images; get_boot_image; bootstrap ;; *) usage ;; esac From 7ad7a89a2bb47412252d4bcb47c9b4b7e31e3df2 Mon Sep 17 00:00:00 2001 From: Doug Coleman Date: Sat, 2 Feb 2008 23:27:27 -0600 Subject: [PATCH 10/16] move >Upper and >Upper-dashes to unicode.case --- extra/strings/lib/lib.factor | 8 -------- extra/unicode/case/case.factor | 11 ++++++++++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/extra/strings/lib/lib.factor b/extra/strings/lib/lib.factor index 719881b768..d0a34c8d28 100644 --- a/extra/strings/lib/lib.factor +++ b/extra/strings/lib/lib.factor @@ -4,11 +4,3 @@ IN: strings.lib ! : char>digit ( c -- i ) 48 - ; ! : string>digits ( s -- seq ) [ char>digit ] { } map-as ; - -! : >Upper ( str -- str ) -! dup empty? [ -! unclip ch>upper 1string swap append -! ] unless ; - -! : >Upper-dashes ( str -- str ) -! "-" split [ >Upper ] map "-" join ; diff --git a/extra/unicode/case/case.factor b/extra/unicode/case/case.factor index ee9e2a0381..f244192a32 100755 --- a/extra/unicode/case/case.factor +++ b/extra/unicode/case/case.factor @@ -1,6 +1,6 @@ USING: kernel unicode.data sequences sequences.next namespaces assocs.lib unicode.normalize math unicode.categories combinators -assocs ; +assocs strings splitting ; IN: unicode.case : ch>lower ( ch -- lower ) simple-lower at-default ; @@ -110,3 +110,12 @@ SYMBOL: locale ! Just casing locale, or overall? dup >title = ; : case-fold? ( string -- ? ) dup >case-fold = ; + + +: >Upper ( str -- str ) + dup empty? [ + unclip ch>upper 1string swap append + ] unless ; + +: >Upper-dashes ( str -- str ) + "-" split [ >Upper ] map "-" join ; From 7954bc33bfec8694e0d619b59c07215b98548a70 Mon Sep 17 00:00:00 2001 From: Doug Coleman Date: Sat, 2 Feb 2008 23:27:44 -0600 Subject: [PATCH 11/16] fix server responders --- extra/http/server/responders/responders.factor | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/extra/http/server/responders/responders.factor b/extra/http/server/responders/responders.factor index a507a95a14..6df52997e1 100644 --- a/extra/http/server/responders/responders.factor +++ b/extra/http/server/responders/responders.factor @@ -2,7 +2,7 @@ ! See http://factorcode.org/license.txt for BSD license. USING: arrays assocs hashtables html html.elements splitting http io kernel math math.parser namespaces parser sequences -strings io.server vectors vector-hash strings.lib ; +strings io.server vectors assocs.lib unicode.case ; IN: http.server.responders @@ -10,11 +10,11 @@ IN: http.server.responders SYMBOL: vhosts SYMBOL: responders -: >header ( value key -- vector-hash ) - VH{ } clone [ set-at ] keep ; +: >header ( value key -- multi-hash ) + H{ } clone [ insert-at ] keep ; : print-header ( alist -- ) - [ swap >Upper-dashes write ": " write print ] vector-hash-each nl ; + [ swap >Upper-dashes write ": " write print ] multi-assoc-each nl ; : response ( msg -- ) "HTTP/1.0 " write print ; @@ -23,7 +23,7 @@ SYMBOL: responders : error-head ( error -- ) dup log-error response - VH{ { "Content-Type" "text/html" } } print-header nl ; + H{ { "Content-Type" V{ "text/html" } } } print-header nl ; : httpd-error ( error -- ) #! This must be run from handle-request @@ -94,7 +94,7 @@ SYMBOL: max-post-request } member? ] assoc-subset [ ": " swap 3append log-message - ] vector-hash-each ; + ] multi-assoc-each ; : prepare-url ( url -- url ) #! This is executed in the with-request namespace. From 2c1bad2254b67b11b4780e537a832580dcdd1660 Mon Sep 17 00:00:00 2001 From: Doug Coleman Date: Sat, 2 Feb 2008 23:28:33 -0600 Subject: [PATCH 12/16] improve the db protocol and update sqlite to use it --- extra/db/db.factor | 58 ++++++++++++++------------- extra/db/postgresql/postgresql.factor | 47 ++++++++++++---------- extra/db/sqlite/sqlite-tests.factor | 41 +++++++++++-------- extra/db/sqlite/sqlite.factor | 49 ++++++++++++---------- 4 files changed, 108 insertions(+), 87 deletions(-) diff --git a/extra/db/db.factor b/extra/db/db.factor index 597ac1f0f3..813ce901ff 100644 --- a/extra/db/db.factor +++ b/extra/db/db.factor @@ -12,30 +12,20 @@ C: db ( handle -- obj ) GENERIC: db-open ( db -- ) GENERIC: db-close ( db -- ) -TUPLE: statement sql params handle bound? n max ; +TUPLE: statement sql params handle bound? ; TUPLE: simple-statement ; -TUPLE: bound-statement ; TUPLE: prepared-statement ; -TUPLE: prepared-bound-statement ; HOOK: db ( str -- statement ) -HOOK: db ( str obj -- statement ) HOOK: db ( str -- statement ) -HOOK: 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 -- ) +GENERIC: execute-statement ( statement -- ) + : bind-statement ( obj statement -- ) 2dup dup statement-bound? [ rebind-statement @@ -45,7 +35,24 @@ GENERIC: rebind-statement ( obj statement -- ) tuck set-statement-params t swap set-statement-bound? ; -: sql-row ( statement -- seq ) +TUPLE: result-set sql params handle n max ; + +GENERIC: query-results ( query -- result-set ) + +GENERIC: #rows ( result-set -- n ) +GENERIC: #columns ( result-set -- n ) +GENERIC# row-column 1 ( result-set n -- obj ) +GENERIC: advance-row ( result-set -- ? ) + +: ( query handle tuple -- result-set ) + >r >r { statement-sql statement-params } get-slots r> + { + set-result-set-sql + set-result-set-params + set-result-set-handle + } result-set construct r> construct-delegate ; + +: sql-row ( result-set -- seq ) dup #columns [ row-column ] with map ; : query-each ( statement quot -- ) @@ -64,23 +71,20 @@ GENERIC: rebind-statement ( obj statement -- ) [ db swap with-variable ] curry with-disposal ] with-scope ; -: do-statement ( statement -- ) - [ advance-row drop ] with-disposal ; +: do-query ( query -- result-set ) + query-results [ [ sql-row ] query-map ] with-disposal ; -: do-query ( query -- rows ) - [ [ sql-row ] query-map ] with-disposal ; +: do-bound-query ( obj query -- rows ) + [ bind-statement ] keep do-query ; -: do-simple-query ( sql -- rows ) - do-query ; +: do-bound-command ( obj query -- rows ) + [ bind-statement ] keep execute-statement ; -: do-bound-query ( sql obj -- rows ) - do-query ; +: sql-query ( sql -- rows ) + [ do-query ] with-disposal ; -: do-simple-command ( sql -- ) - do-statement ; - -: do-bound-command ( sql obj -- ) - do-statement ; +: sql-command ( sql -- ) + [ execute-statement ] with-disposal ; SYMBOL: in-transaction HOOK: begin-transaction db ( -- ) diff --git a/extra/db/postgresql/postgresql.factor b/extra/db/postgresql/postgresql.factor index cd2c34682e..2ea1b3a1dc 100644 --- a/extra/db/postgresql/postgresql.factor +++ b/extra/db/postgresql/postgresql.factor @@ -38,32 +38,41 @@ M: postgresql-db dispose ( db -- ) : with-postgresql ( host ust pass db quot -- ) >r r> with-disposal ; -M: postgresql-statement #rows ( statement -- n ) + +M: postgresql-result-set #rows ( statement -- n ) statement-handle PQntuples ; -M: postgresql-statement #columns ( statement -- n ) +M: postgresql-result-set #columns ( statement -- n ) statement-handle PQnfields ; -M: postgresql-statement row-column ( statement n -- obj ) +M: postgresql-result-set 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 + +: init-result-set ( result-set -- ) + dup result-set-max [ + dup do-postgresql-statement over set-result-set-handle + dup #rows over set-result-set-max + -1 over set-result-set-n ] unless drop ; -: increment-n ( statement -- n ) - dup statement-n 1+ dup rot set-statement-n ; +: increment-n ( result-set -- n ) + dup result-set-n 1+ dup rot set-result-set-n ; + +M: postgresql-result-set advance-row ( result-set -- ? ) + dup init-result-set + dup increment-n swap result-set-max >= ; -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 ; + f swap set-statement-handle ; + +M: postgresql-result-set dispose ( result-set -- ) + dup result-set-handle PQclear + 0 0 f roll { + set-statement-n set-statement-max set-statement-handle + } set-slots ; M: postgresql-statement prepare-statement ( statement -- ) [ @@ -76,12 +85,6 @@ M: postgresql-db ( sql -- statement ) { set-statement-sql } statement construct ; -M: postgresql-db ( sql array -- statement ) - { set-statement-sql set-statement-params } statement construct - ; - M: postgresql-db ( sql -- statement ) - ; - -M: postgresql-db ( sql seq -- statement ) - ; + { set-statement-sql } statement construct + ; diff --git a/extra/db/sqlite/sqlite-tests.factor b/extra/db/sqlite/sqlite-tests.factor index 79e967de24..ef1bbfc262 100644 --- a/extra/db/sqlite/sqlite-tests.factor +++ b/extra/db/sqlite/sqlite-tests.factor @@ -26,20 +26,27 @@ IN: temporary { "John" "America" } { "Jane" "New Zealand" } } -] [ test.db [ "select * from person" do-simple-query ] with-sqlite ] unit-test +] [ + "extra/db/sqlite/test.db" resource-path [ + "select * from person" sql-query + ] with-sqlite +] unit-test [ { { "John" "America" } } ] [ - test.db [ + "extra/db/sqlite/test.db" resource-path [ "select * from person where name = :name and country = :country" - { { ":name" "Jane" } { ":country" "New Zealand" } } - dup [ sql-row ] query-map + [ + { { ":name" "Jane" } { ":country" "New Zealand" } } + over do-bound-query - { { "Jane" "New Zealand" } } = [ "test fails" throw ] unless - { { ":name" "John" } { ":country" "America" } } over bind-statement + { { "Jane" "New Zealand" } } = + [ "test fails" throw ] unless - dup [ sql-row ] query-map swap dispose + { { ":name" "John" } { ":country" "America" } } + swap do-bound-query + ] with-disposal ] with-sqlite ] unit-test @@ -48,13 +55,13 @@ IN: temporary { "1" "John" "America" } { "2" "Jane" "New Zealand" } } -] [ test.db [ "select rowid, * from person" do-simple-query ] with-sqlite ] unit-test +] [ test.db [ "select rowid, * from person" sql-query ] with-sqlite ] unit-test [ ] [ "extra/db/sqlite/test.db" resource-path [ "insert into person(name, country) values('Jimmy', 'Canada')" - do-simple-command + sql-command ] with-sqlite ] unit-test @@ -64,13 +71,13 @@ IN: temporary { "2" "Jane" "New Zealand" } { "3" "Jimmy" "Canada" } } -] [ test.db [ "select rowid, * from person" do-simple-query ] with-sqlite ] unit-test +] [ test.db [ "select rowid, * from person" sql-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 + "insert into person(name, country) values('Jose', 'Mexico')" sql-command + "insert into person(name, country) values('Jose', 'Mexico')" sql-command "oops" throw ] with-transaction ] with-sqlite @@ -78,7 +85,7 @@ IN: temporary [ 3 ] [ "extra/db/sqlite/test.db" resource-path [ - "select * from person" do-simple-query length + "select * from person" sql-query length ] with-sqlite ] unit-test @@ -86,14 +93,16 @@ IN: temporary ] [ "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 + "insert into person(name, country) values('Jose', 'Mexico')" + sql-command + "insert into person(name, country) values('Jose', 'Mexico')" + sql-command ] with-transaction ] with-sqlite ] unit-test [ 5 ] [ "extra/db/sqlite/test.db" resource-path [ - "select * from person" do-simple-query length + "select * from person" sql-query length ] with-sqlite ] unit-test diff --git a/extra/db/sqlite/sqlite.factor b/extra/db/sqlite/sqlite.factor index c5964ed599..8352d2e11f 100644 --- a/extra/db/sqlite/sqlite.factor +++ b/extra/db/sqlite/sqlite.factor @@ -1,9 +1,9 @@ ! 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 ; +USING: alien arrays assocs classes compiler db db.sql +hashtables io.files kernel math math.parser namespaces +prettyprint sequences strings tuples alien.c-types +continuations db.sqlite.lib db.sqlite.ffi ; IN: db.sqlite TUPLE: sqlite-db path ; @@ -24,47 +24,52 @@ M: sqlite-db dispose ( obj -- ) TUPLE: sqlite-statement ; C: sqlite-statement +TUPLE: sqlite-result-set ; +: ( query -- sqlite-result-set ) + dup statement-handle sqlite-result-set ; + M: sqlite-db ( str -- obj ) ; -M: sqlite-db ( str -- obj ) - ; - M: sqlite-db ( str -- obj ) db get db-handle over sqlite-prepare { set-statement-sql set-statement-handle } statement construct [ set-delegate ] keep ; -M: sqlite-db ( str assoc -- obj ) - swap tuck bind-statement ; - M: sqlite-statement dispose ( statement -- ) statement-handle sqlite-finalize ; +M: sqlite-result-set dispose ( result-set -- ) + f swap set-result-set-handle ; + M: sqlite-statement bind-statement* ( assoc statement -- ) statement-handle swap sqlite-bind-assoc ; M: sqlite-statement rebind-statement ( assoc statement -- ) - dup reset-statement + dup statement-handle sqlite-reset statement-handle swap sqlite-bind-assoc ; -M: sqlite-statement #columns ( statement -- n ) - statement-handle sqlite-#columns ; +M: sqlite-statement execute-statement ( statement -- ) + statement-handle sqlite-next drop ; -M: sqlite-statement row-column ( statement n -- obj ) - >r statement-handle r> sqlite-column ; +M: sqlite-result-set #columns ( result-set -- n ) + result-set-handle sqlite-#columns ; -M: sqlite-statement advance-row ( statement -- ? ) - statement-handle sqlite-next ; +M: sqlite-result-set row-column ( result-set n -- obj ) + >r result-set-handle r> sqlite-column ; + +M: sqlite-result-set advance-row ( result-set -- handle ? ) + result-set-handle sqlite-next ; + +M: sqlite-statement query-results ( query -- result-set ) + dup statement-handle sqlite-result-set ; -M: sqlite-statement reset-statement ( statement -- ) - statement-handle sqlite-reset ; M: sqlite-db begin-transaction ( -- ) - "BEGIN" do-simple-command ; + "BEGIN" sql-command ; M: sqlite-db commit-transaction ( -- ) - "COMMIT" do-simple-command ; + "COMMIT" sql-command ; M: sqlite-db rollback-transaction ( -- ) - "ROLLBACK" do-simple-command ; + "ROLLBACK" sql-command ; From 55cfd30543091c74889b1c8a0ae9a3838377f783 Mon Sep 17 00:00:00 2001 From: Doug Coleman Date: Sat, 2 Feb 2008 23:46:56 -0600 Subject: [PATCH 13/16] remove strings.lib from automata --- extra/automata/automata.factor | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extra/automata/automata.factor b/extra/automata/automata.factor index 732033fb75..cd799d477e 100644 --- a/extra/automata/automata.factor +++ b/extra/automata/automata.factor @@ -1,6 +1,6 @@ USING: kernel math math.parser random arrays hashtables assocs sequences - vars strings.lib ; + vars ; IN: automata @@ -108,4 +108,4 @@ last-line> height> [ drop step-capped-line dup ] map >bitmap >last-line ; ! : start-loop ( -- ) t >loop-flag [ loop ] in-thread ; -! : stop-loop ( -- ) f >loop-flag ; \ No newline at end of file +! : stop-loop ( -- ) f >loop-flag ; From 1b03538caa28f37c4e56986c9e22eae9fcf4d966 Mon Sep 17 00:00:00 2001 From: Doug Coleman Date: Sun, 3 Feb 2008 00:14:27 -0600 Subject: [PATCH 14/16] fix compile errors in sqlite --- extra/db/db.factor | 2 +- extra/db/sqlite/ffi/ffi.factor | 1 - extra/db/sqlite/lib/lib.factor | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/extra/db/db.factor b/extra/db/db.factor index 813ce901ff..81d79eb695 100644 --- a/extra/db/db.factor +++ b/extra/db/db.factor @@ -77,7 +77,7 @@ GENERIC: advance-row ( result-set -- ? ) : do-bound-query ( obj query -- rows ) [ bind-statement ] keep do-query ; -: do-bound-command ( obj query -- rows ) +: do-bound-command ( obj query -- ) [ bind-statement ] keep execute-statement ; : sql-query ( sql -- rows ) diff --git a/extra/db/sqlite/ffi/ffi.factor b/extra/db/sqlite/ffi/ffi.factor index 77a86a8a2d..609c597b35 100644 --- a/extra/db/sqlite/ffi/ffi.factor +++ b/extra/db/sqlite/ffi/ffi.factor @@ -109,7 +109,6 @@ 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 ) ; diff --git a/extra/db/sqlite/lib/lib.factor b/extra/db/sqlite/lib/lib.factor index 99cd9c1b9f..4e4f2ca508 100644 --- a/extra/db/sqlite/lib/lib.factor +++ b/extra/db/sqlite/lib/lib.factor @@ -80,7 +80,7 @@ TUPLE: sqlite-error n message ; sqlite-step ] if ; -: sqlite-next ( prepared -- ) +: sqlite-next ( prepared -- ? ) sqlite3_step step-complete? ; : sqlite-each ( statement quot -- ) From bb1e06dd8d812db71bb802b0faa9d5fae70b0571 Mon Sep 17 00:00:00 2001 From: Doug Coleman Date: Sun, 3 Feb 2008 15:06:31 -0600 Subject: [PATCH 15/16] add copyright notices update postgresql for new db protocol make unit tests pass --- extra/db/db.factor | 4 + extra/db/postgresql/ffi/ffi.factor | 4 +- extra/db/postgresql/lib/lib.factor | 60 +++------ extra/db/postgresql/postgresql-tests.factor | 128 ++++++++++++++------ extra/db/postgresql/postgresql.factor | 57 +++++---- extra/db/sqlite/lib/lib.factor | 22 +--- extra/db/sqlite/sqlite-tests.factor | 18 +-- extra/db/sqlite/sqlite.factor | 1 - 8 files changed, 161 insertions(+), 133 deletions(-) diff --git a/extra/db/db.factor b/extra/db/db.factor index 81d79eb695..b765924cd6 100644 --- a/extra/db/db.factor +++ b/extra/db/db.factor @@ -44,6 +44,10 @@ GENERIC: #columns ( result-set -- n ) GENERIC# row-column 1 ( result-set n -- obj ) GENERIC: advance-row ( result-set -- ? ) +: init-result-set ( result-set -- ) + dup #rows over set-result-set-max + -1 swap set-result-set-n ; + : ( query handle tuple -- result-set ) >r >r { statement-sql statement-params } get-slots r> { diff --git a/extra/db/postgresql/ffi/ffi.factor b/extra/db/postgresql/ffi/ffi.factor index 368e2fbe77..dbaa70c625 100644 --- a/extra/db/postgresql/ffi/ffi.factor +++ b/extra/db/postgresql/ffi/ffi.factor @@ -1,9 +1,7 @@ ! 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 +! tested on debian linux with postgresql 8.1 USING: alien alien.syntax combinators system ; IN: db.postgresql.ffi diff --git a/extra/db/postgresql/lib/lib.factor b/extra/db/postgresql/lib/lib.factor index 4b362f9931..a940a42ae4 100644 --- a/extra/db/postgresql/lib/lib.factor +++ b/extra/db/postgresql/lib/lib.factor @@ -1,13 +1,9 @@ +! Copyright (C) 2008 Doug Coleman. +! See http://factorcode.org/license.txt for BSD license. USING: arrays continuations db io kernel math namespaces -quotations sequences db.postgresql.ffi ; +quotations sequences db.postgresql.ffi alien alien.c-types ; 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 @@ -28,45 +24,21 @@ SYMBOL: query-res PQresultStatus PGRES_COMMAND_OK PGRES_TUPLES_OK 2array member? ; +: connect-postgres ( host port pgopts pgtty db user pass -- conn ) + PQsetdbLogin + dup PQstatus zero? [ postgresql-error-message throw ] unless ; + : 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 ; +: do-postgresql-bound-statement ( statement -- res ) + >r db get db-handle r> + [ statement-sql ] keep + [ statement-params length f ] keep + statement-params [ malloc-char-string ] map >c-void*-array + f f 0 PQexecParams + dup postgresql-result-ok? [ + dup postgresql-result-error-message swap PQclear throw + ] unless ; diff --git a/extra/db/postgresql/postgresql-tests.factor b/extra/db/postgresql/postgresql-tests.factor index 438a80e2d8..c5a5155d12 100644 --- a/extra/db/postgresql/postgresql-tests.factor +++ b/extra/db/postgresql/postgresql-tests.factor @@ -2,53 +2,109 @@ ! Set username and password in the 'connect' word. USING: kernel db.postgresql alien continuations io prettyprint -sequences namespaces tools.test ; +sequences namespaces tools.test db ; IN: temporary -: test-connection ( host port pgopts pgtty db user pass -- bool ) - [ [ ] with-postgres ] catch "Error connecting!" "Connected!" ? print ; +IN: scratchpad +: test-db ( -- postgresql-db ) + "localhost" "postgres" "" "factor-test" ; +IN: temporary -[ ] [ "localhost" "" "" "" "factor-test" "postgres" "" test-connection ] unit-test +[ ] [ test-db [ ] with-db ] unit-test -[ ] [ "localhost" "postgres" "" "factor-test" [ ] with-db ] unit-test +[ ] [ + test-db [ + [ "drop table person;" sql-command ] catch drop + "create table person (name varchar(30), country varchar(30));" + sql-command -! just a basic demo + "insert into person values('John', 'America');" sql-command + "insert into person values('Jane', 'New Zealand');" sql-command + ] with-db +] unit-test -"localhost" "postgres" "" "factor-test" [ - [ ] [ "drop table animal" do-command ] unit-test +[ + { + { "John" "America" } + { "Jane" "New Zealand" } + } +] [ + test-db [ + "select * from person" sql-query + ] with-db +] 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 +[ + { { "John" "America" } } +] [ + test-db [ + "select * from person where name = $1 and country = $2" + [ + { "Jane" "New Zealand" } + over do-bound-query - [ ] [ "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 + { { "Jane" "New Zealand" } } = + [ "test fails" throw ] unless - [ ] [ "insert into animal (species, name, age) values ('lion', 'Simba', 1)" - do-command ] unit-test + { "John" "America" } + swap do-bound-query + ] with-disposal + ] with-db +] unit-test - [ ] [ - "select * from animal" +[ + { + { "John" "America" } + { "Jane" "New Zealand" } + } +] [ test-db [ "select * from person" sql-query ] with-db ] unit-test + +[ +] [ + test-db [ + "insert into person(name, country) values('Jimmy', 'Canada')" + sql-command + ] with-db +] unit-test + +[ + { + { "John" "America" } + { "Jane" "New Zealand" } + { "Jimmy" "Canada" } + } +] [ test-db [ "select * from person" sql-query ] with-db ] unit-test + +[ + test-db [ [ - "Animal table:" print - result>seq print-table - ] do-query - ] unit-test + "insert into person(name, country) values('Jose', 'Mexico')" sql-command + "insert into person(name, country) values('Jose', 'Mexico')" sql-command + "oops" throw + ] with-transaction + ] with-db +] unit-test-fails - ! intentional errors - ! [ "select asdf from animal" - ! [ ] do-query ] catch [ "caught: " write print ] when* - ! "select asdf from animal" [ ] do-query - ! "aofijweafew" do-command -] with-db +[ 3 ] [ + test-db [ + "select * from person" sql-query length + ] with-db +] unit-test +[ +] [ + test-db [ + [ + "insert into person(name, country) values('Jose', 'Mexico')" + sql-command + "insert into person(name, country) values('Jose', 'Mexico')" + sql-command + ] with-transaction + ] with-db +] unit-test -"localhost" "postgres" "" "factor-test" [ - [ ] [ "drop table animal" do-command ] unit-test -] with-db +[ 5 ] [ + test-db [ + "select * from person" sql-query length + ] with-db +] unit-test diff --git a/extra/db/postgresql/postgresql.factor b/extra/db/postgresql/postgresql.factor index 2ea1b3a1dc..df778cc80d 100644 --- a/extra/db/postgresql/postgresql.factor +++ b/extra/db/postgresql/postgresql.factor @@ -1,8 +1,5 @@ -! Copyright (C) 2007 Doug Coleman. +! Copyright (C) 2007, 2008 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 ; @@ -10,6 +7,7 @@ IN: db.postgresql TUPLE: postgresql-db host port pgopts pgtty db user pass ; TUPLE: postgresql-statement ; +TUPLE: postgresql-result-set ; : ( statement -- postgresql-statement ) postgresql-statement construct-delegate ; @@ -38,31 +36,39 @@ M: postgresql-db dispose ( db -- ) : with-postgresql ( host ust pass db quot -- ) >r r> with-disposal ; +M: postgresql-statement bind-statement* ( seq statement -- ) + set-statement-params ; -M: postgresql-result-set #rows ( statement -- n ) - statement-handle PQntuples ; +M: postgresql-statement rebind-statement ( seq statement -- ) + bind-statement* ; -M: postgresql-result-set #columns ( statement -- n ) - statement-handle PQnfields ; +M: postgresql-result-set #rows ( result-set -- n ) + result-set-handle PQntuples ; -M: postgresql-result-set row-column ( statement n -- obj ) - >r dup statement-handle swap statement-n r> PQgetvalue ; +M: postgresql-result-set #columns ( result-set -- n ) + result-set-handle PQnfields ; +M: postgresql-result-set row-column ( result-set n -- obj ) + >r dup result-set-handle swap result-set-n r> PQgetvalue ; -: init-result-set ( result-set -- ) - dup result-set-max [ - dup do-postgresql-statement over set-result-set-handle - dup #rows over set-result-set-max - -1 over set-result-set-n - ] unless drop ; +M: postgresql-statement execute-statement ( statement -- ) + query-results dispose ; : increment-n ( result-set -- n ) dup result-set-n 1+ dup rot set-result-set-n ; -M: postgresql-result-set advance-row ( result-set -- ? ) - dup init-result-set - dup increment-n swap result-set-max >= ; +M: postgresql-statement query-results ( query -- result-set ) + dup statement-params [ + over [ bind-statement ] keep + do-postgresql-bound-statement + ] [ + dup do-postgresql-statement + ] if* + postgresql-result-set + dup init-result-set ; +M: postgresql-result-set advance-row ( result-set -- ? ) + dup increment-n swap result-set-max >= ; M: postgresql-statement dispose ( query -- ) dup statement-handle PQclear @@ -71,14 +77,14 @@ M: postgresql-statement dispose ( query -- ) M: postgresql-result-set dispose ( result-set -- ) dup result-set-handle PQclear 0 0 f roll { - set-statement-n set-statement-max set-statement-handle + set-result-set-n set-result-set-max set-result-set-handle } 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 + length f PQprepare postgresql-error ] keep set-statement-handle ; M: postgresql-db ( sql -- statement ) @@ -88,3 +94,12 @@ M: postgresql-db ( sql -- statement ) M: postgresql-db ( sql -- statement ) { set-statement-sql } statement construct ; + +M: postgresql-db begin-transaction ( -- ) + "BEGIN" sql-command ; + +M: postgresql-db commit-transaction ( -- ) + "COMMIT" sql-command ; + +M: postgresql-db rollback-transaction ( -- ) + "ROLLBACK" sql-command ; diff --git a/extra/db/sqlite/lib/lib.factor b/extra/db/sqlite/lib/lib.factor index 4e4f2ca508..e5f8425d92 100644 --- a/extra/db/sqlite/lib/lib.factor +++ b/extra/db/sqlite/lib/lib.factor @@ -1,3 +1,5 @@ +! Copyright (C) 2008 Chris Double, Doug Coleman. +! See http://factorcode.org/license.txt for BSD license. USING: alien.c-types assocs kernel math math.parser sequences db.sqlite.ffi ; IN: db.sqlite.lib @@ -65,7 +67,6 @@ TUPLE: sqlite-error n message ; ! SQLITE_BLOB 4 ! SQLITE_NULL 5 - : step-complete? ( step-result -- bool ) dup SQLITE_ROW = [ drop f @@ -82,22 +83,3 @@ TUPLE: sqlite-error n message ; : 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) ; diff --git a/extra/db/sqlite/sqlite-tests.factor b/extra/db/sqlite/sqlite-tests.factor index ef1bbfc262..f64b8d1104 100644 --- a/extra/db/sqlite/sqlite-tests.factor +++ b/extra/db/sqlite/sqlite-tests.factor @@ -5,12 +5,14 @@ IN: temporary ! "sqlite3 -init test.txt test.db" +IN: scratchpad : test.db "extra/db/sqlite/test.db" resource-path ; +IN: temporary : (create-db) ( -- str ) [ "sqlite3 -init " % - "extra/db/sqlite/test.txt" resource-path % + test.db % " " % test.db % ] "" make ; @@ -27,7 +29,7 @@ IN: temporary { "Jane" "New Zealand" } } ] [ - "extra/db/sqlite/test.db" resource-path [ + test.db [ "select * from person" sql-query ] with-sqlite ] unit-test @@ -35,7 +37,7 @@ IN: temporary [ { { "John" "America" } } ] [ - "extra/db/sqlite/test.db" resource-path [ + test.db [ "select * from person where name = :name and country = :country" [ { { ":name" "Jane" } { ":country" "New Zealand" } } @@ -59,7 +61,7 @@ IN: temporary [ ] [ - "extra/db/sqlite/test.db" resource-path [ + test.db [ "insert into person(name, country) values('Jimmy', 'Canada')" sql-command ] with-sqlite @@ -74,7 +76,7 @@ IN: temporary ] [ test.db [ "select rowid, * from person" sql-query ] with-sqlite ] unit-test [ - "extra/db/sqlite/test.db" resource-path [ + test.db [ [ "insert into person(name, country) values('Jose', 'Mexico')" sql-command "insert into person(name, country) values('Jose', 'Mexico')" sql-command @@ -84,14 +86,14 @@ IN: temporary ] unit-test-fails [ 3 ] [ - "extra/db/sqlite/test.db" resource-path [ + test.db [ "select * from person" sql-query length ] with-sqlite ] unit-test [ ] [ - "extra/db/sqlite/test.db" resource-path [ + test.db [ [ "insert into person(name, country) values('Jose', 'Mexico')" sql-command @@ -102,7 +104,7 @@ IN: temporary ] unit-test [ 5 ] [ - "extra/db/sqlite/test.db" resource-path [ + test.db [ "select * from person" sql-query length ] with-sqlite ] unit-test diff --git a/extra/db/sqlite/sqlite.factor b/extra/db/sqlite/sqlite.factor index 8352d2e11f..49462dcc50 100644 --- a/extra/db/sqlite/sqlite.factor +++ b/extra/db/sqlite/sqlite.factor @@ -64,7 +64,6 @@ M: sqlite-result-set advance-row ( result-set -- handle ? ) M: sqlite-statement query-results ( query -- result-set ) dup statement-handle sqlite-result-set ; - M: sqlite-db begin-transaction ( -- ) "BEGIN" sql-command ; From d6185e224ad77ec30490086c101536bcdd4eed7e Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sun, 3 Feb 2008 16:13:57 -0600 Subject: [PATCH 16/16] Undo funny stuff --- extra/http/server/responders/responders.factor | 4 ++-- extra/unicode/case/case.factor | 9 --------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/extra/http/server/responders/responders.factor b/extra/http/server/responders/responders.factor index 6df52997e1..70503236f6 100644 --- a/extra/http/server/responders/responders.factor +++ b/extra/http/server/responders/responders.factor @@ -2,7 +2,7 @@ ! See http://factorcode.org/license.txt for BSD license. USING: arrays assocs hashtables html html.elements splitting http io kernel math math.parser namespaces parser sequences -strings io.server vectors assocs.lib unicode.case ; +strings io.server vectors assocs.lib ; IN: http.server.responders @@ -14,7 +14,7 @@ SYMBOL: responders H{ } clone [ insert-at ] keep ; : print-header ( alist -- ) - [ swap >Upper-dashes write ": " write print ] multi-assoc-each nl ; + [ swap write ": " write print ] multi-assoc-each nl ; : response ( msg -- ) "HTTP/1.0 " write print ; diff --git a/extra/unicode/case/case.factor b/extra/unicode/case/case.factor index f244192a32..8129ec17f8 100755 --- a/extra/unicode/case/case.factor +++ b/extra/unicode/case/case.factor @@ -110,12 +110,3 @@ SYMBOL: locale ! Just casing locale, or overall? dup >title = ; : case-fold? ( string -- ? ) dup >case-fold = ; - - -: >Upper ( str -- str ) - dup empty? [ - unclip ch>upper 1string swap append - ] unless ; - -: >Upper-dashes ( str -- str ) - "-" split [ >Upper ] map "-" join ;