]> gitweb.factorcode.org Git - factor.git/commitdiff
initial libs/sql
authorerg <erg@trifocus.net>
Fri, 15 Dec 2006 09:06:17 +0000 (09:06 +0000)
committererg <erg@trifocus.net>
Fri, 15 Dec 2006 09:06:17 +0000 (09:06 +0000)
17 files changed:
libs/sql/execute.factor [new file with mode: 0644]
libs/sql/load.factor [new file with mode: 0644]
libs/sql/mappings.factor [new file with mode: 0644]
libs/sql/postgresql/execute.factor [new file with mode: 0644]
libs/sql/postgresql/libpq.factor [new file with mode: 0644]
libs/sql/postgresql/postgresql.factor [new file with mode: 0644]
libs/sql/postgresql/simple.factor [new file with mode: 0644]
libs/sql/simple-bind.factor [new file with mode: 0644]
libs/sql/simple.factor [new file with mode: 0644]
libs/sql/sql.factor [new file with mode: 0644]
libs/sql/sqlite/execute.factor [new file with mode: 0644]
libs/sql/sqlite/libsqlite.factor [new file with mode: 0644]
libs/sql/sqlite/load.factor [new file with mode: 0644]
libs/sql/sqlite/simple.factor [new file with mode: 0644]
libs/sql/sqlite/sqlite.factor [new file with mode: 0644]
libs/sql/tupledb.factor [new file with mode: 0644]
libs/sql/utils.factor [new file with mode: 0644]

diff --git a/libs/sql/execute.factor b/libs/sql/execute.factor
new file mode 100644 (file)
index 0000000..00ad163
--- /dev/null
@@ -0,0 +1,10 @@
+USING: kernel namespaces ;\r
+IN: sql\r
+\r
+GENERIC: execute-sql* ( string db -- )\r
+GENERIC: query-sql* ( string db -- seq )\r
+\r
+: execute-sql ( string -- ) db get execute-sql* ;\r
+: query-sql ( string -- ) db get query-sql* ;\r
+\r
+\r
diff --git a/libs/sql/load.factor b/libs/sql/load.factor
new file mode 100644 (file)
index 0000000..847cb54
--- /dev/null
@@ -0,0 +1,27 @@
+PROVIDE: libs/sql\r
+{ +files+ {\r
+    "sql.factor"\r
+    "utils.factor"\r
+    "simple.factor"\r
+    "mappings.factor"\r
+    "execute.factor"\r
+\r
+    "sqlite/libsqlite.factor"\r
+    "sqlite/sqlite.factor"\r
+    "sqlite/simple.factor"\r
+    "sqlite/execute.factor"\r
+    "postgresql/libpq.factor"\r
+    "postgresql/postgresql.factor"\r
+    "postgresql/simple.factor"\r
+    "postgresql/execute.factor"\r
+\r
+    "tupledb.factor"\r
+\r
+    "thewebsite.factor"\r
+} }\r
+{ +tests+ {\r
+    "test/data.factor"\r
+    "test/insert.factor"\r
+    "test/util.factor"\r
+} } ;\r
+\r
diff --git a/libs/sql/mappings.factor b/libs/sql/mappings.factor
new file mode 100644 (file)
index 0000000..e648a2b
--- /dev/null
@@ -0,0 +1 @@
+IN: sql\r
diff --git a/libs/sql/postgresql/execute.factor b/libs/sql/postgresql/execute.factor
new file mode 100644 (file)
index 0000000..cff5fcb
--- /dev/null
@@ -0,0 +1 @@
+IN: postgresql\r
diff --git a/libs/sql/postgresql/libpq.factor b/libs/sql/postgresql/libpq.factor
new file mode 100644 (file)
index 0000000..3786d1d
--- /dev/null
@@ -0,0 +1,353 @@
+! See http://factor.sf.net/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
+
+IN: postgresql
+USING: alien ;
+
+
+! 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/libs/sql/postgresql/postgresql.factor b/libs/sql/postgresql/postgresql.factor
new file mode 100644 (file)
index 0000000..54e73cc
--- /dev/null
@@ -0,0 +1,60 @@
+! See http://factor.sf.net/license.txt for BSD license.
+
+! adapted from libpq-fe.h version 7.4.7
+! tested on debian linux with postgresql 7.4.7
+
+IN: postgresql
+USING: kernel alien errors io prettyprint sequences namespaces arrays math sql ;
+
+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
+
+: with-postgres-catch ( host port pgopts pgtty db user pass quot -- )
+    [ with-postgres ] catch [ "caught: " write print ] when* ;
+
+: 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 -- )
+    unit \ (do-command) add db get swap call ;
+
+: prepare ( str quot word -- conn quot )
+    rot unit 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 ] map-with ] map-with ;
+
+: print-table ( seq -- )
+    [ [ write bl ] each "\n" write ] each ;
+
diff --git a/libs/sql/postgresql/simple.factor b/libs/sql/postgresql/simple.factor
new file mode 100644 (file)
index 0000000..cff5fcb
--- /dev/null
@@ -0,0 +1 @@
+IN: postgresql\r
diff --git a/libs/sql/simple-bind.factor b/libs/sql/simple-bind.factor
new file mode 100644 (file)
index 0000000..124fb86
--- /dev/null
@@ -0,0 +1,4 @@
+USING: kernel ;\r
+IN: sql\r
+\r
+\r
diff --git a/libs/sql/simple.factor b/libs/sql/simple.factor
new file mode 100644 (file)
index 0000000..10a083f
--- /dev/null
@@ -0,0 +1,52 @@
+USING: generic kernel namespaces prettyprint sequences sql:utils ;\r
+IN: sql\r
+\r
+GENERIC: create-sql* ( tuple db -- string )\r
+GENERIC: drop-sql* ( tuple db -- string )\r
+GENERIC: insert-sql* ( tuple db -- string )\r
+GENERIC: delete-sql* ( tuple db -- string )\r
+GENERIC: update-sql* ( tuple db -- string )\r
+GENERIC: select-sql* ( tuple db -- string )\r
+\r
+: create-sql ( tuple -- string ) db get create-sql* ;\r
+: drop-sql ( tuple -- string ) db get drop-sql* ;\r
+: insert-sql ( tuple -- string ) db get insert-sql* ;\r
+: delete-sql ( tuple -- string ) db get delete-sql* ;\r
+: update-sql ( tuple -- string ) db get update-sql* ;\r
+: select-sql ( tuple -- string ) db get select-sql* ;\r
+\r
+M: connection create-sql* ( tuple db -- string )\r
+    drop [\r
+        "create table " %\r
+        dup class unparse % "(" %\r
+        tuple>mapping%\r
+        ");" %\r
+    ] "" make ;\r
+\r
+M: connection drop-sql* ( tuple db -- string )\r
+    drop [ "drop table " % tuple>sql-name % ";" % ] "" make ;\r
+\r
+M: connection insert-sql* ( tuple db -- string )\r
+    drop [\r
+        "insert into " %\r
+        dup tuple>sql-name %\r
+        " (" % tuple>insert-parts dup first ", " join %\r
+        ") values(" %\r
+        second [ escape-sql enquote ] map ", " join %\r
+        ");" %\r
+    ] "" make ;\r
+\r
+M: connection delete-sql* ( tuple db -- string )\r
+    drop [\r
+        ! "delete from table " % unparse % ";" %\r
+    ] "" make ;\r
+\r
+M: connection update-sql* ( tuples db -- string )\r
+    drop [\r
+    ] "" make ;\r
+\r
+M: connection select-sql* ( tuples db -- string )\r
+    drop [\r
+    ] "" make ;\r
+\r
+\r
diff --git a/libs/sql/sql.factor b/libs/sql/sql.factor
new file mode 100644 (file)
index 0000000..6779939
--- /dev/null
@@ -0,0 +1,10 @@
+USING: kernel namespaces ;\r
+IN: sql\r
+\r
+SYMBOL: db\r
+TUPLE: connection handle ;\r
+\r
+! TESTING\r
+"handle" <connection> db set-global\r
+\r
+\r
diff --git a/libs/sql/sqlite/execute.factor b/libs/sql/sqlite/execute.factor
new file mode 100644 (file)
index 0000000..f75342a
--- /dev/null
@@ -0,0 +1,7 @@
+USING: kernel namespaces sql ;\r
+IN: sqlite\r
+\r
+M: sqlite execute-sql* ( string db -- )\r
+    connection-handle swap\r
+    sqlite-prepare dup [ drop ] sqlite-each sqlite-finalize ;\r
+\r
diff --git a/libs/sql/sqlite/libsqlite.factor b/libs/sql/sqlite/libsqlite.factor
new file mode 100644 (file)
index 0000000..75f3cd6
--- /dev/null
@@ -0,0 +1,111 @@
+! 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: libsqlite
+USING: alien compiler errors kernel math namespaces sequences strings ;
+
+! 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/libs/sql/sqlite/load.factor b/libs/sql/sqlite/load.factor
new file mode 100644 (file)
index 0000000..9818e3b
--- /dev/null
@@ -0,0 +1,11 @@
+PROVIDE: libs/sqlite
+{ +files+ {
+       "libsqlite.factor"
+       "sqlite.factor"
+       "sqlite.facts"
+       "tuple-db.factor"
+       "tuple-db.facts"
+} }
+{ +tests+ {
+       "tuple-db-tests.factor"
+} } ;
diff --git a/libs/sql/sqlite/simple.factor b/libs/sql/sqlite/simple.factor
new file mode 100644 (file)
index 0000000..9c45c17
--- /dev/null
@@ -0,0 +1,24 @@
+USING: generic kernel namespaces prettyprint sql sql:utils ;\r
+IN: sqlite\r
+\r
+TUPLE: sqlite ;\r
+C: sqlite ( path -- db )\r
+    >r sqlite-open <connection> r>\r
+    [ set-delegate ] keep ;\r
+\r
+! M: sqlite insert-sql* ( tuple db -- string )\r
+    #! Insert and fill in the ID column\r
+    ! ;\r
+\r
+M: sqlite delete-sql* ( tuple db -- string )\r
+    #! Delete based on the ID column\r
+    ;\r
+\r
+M: sqlite update-sql* ( tuple db -- string )\r
+    #! Update based on the ID column\r
+    ;\r
+\r
+M: sqlite select-sql* ( tuple db -- string )\r
+    ;\r
+\r
+\r
diff --git a/libs/sql/sqlite/sqlite.factor b/libs/sql/sqlite/sqlite.factor
new file mode 100644 (file)
index 0000000..a5b603a
--- /dev/null
@@ -0,0 +1,126 @@
+! 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.
+! 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
+USING: alien compiler errors libsqlite kernel namespaces sequences sql strings ;
+
+TUPLE: sqlite-error n message ;
+
+! 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> throw
+  ] if ;
+
+: sqlite-open ( filename -- db )
+  #! Open the database referenced by the filename and return
+  #! a handle to that database. An error is thrown if the database
+  #! failed to open.
+  "void*" <c-object> [ sqlite3_open sqlite-check-result ] keep *void* ;
+
+: sqlite-close ( db -- )
+  #! Close the given database
+  sqlite3_close sqlite-check-result ;
+
+: sqlite-last-insert-rowid ( db -- rowid )
+  #! Return the rowid of the last insert
+  sqlite3_last_insert_rowid ;
+
+: sqlite-prepare ( db sql -- statement )
+  #! Prepare a SQL statement. Returns the statement which
+  #! can have values bound to parameters or simply executed.
+  #! TODO: Support multiple statements in the SQL string.
+  dup length "void*" <c-object> "void*" <c-object>
+  [ sqlite3_prepare sqlite-check-result ] 2keep
+  drop *void* ;
+
+: sqlite-bind-text ( statement index text -- )
+  #! Bind the text to the parameterized value in the statement.  
+  dup length SQLITE_TRANSIENT sqlite3_bind_text sqlite-check-result ;
+
+: sqlite-bind-parameter-index ( statement name -- index )
+  sqlite3_bind_parameter_index ;
+
+: sqlite-bind-text-by-name ( statement name text -- )
+  >r dupd sqlite-bind-parameter-index r> sqlite-bind-text ;
+
+: sqlite-finalize ( statement -- )
+  #! Clean up all resources related to a statement. Once called
+  #! the statement cannot be used. All statements must be finalized
+  #! before closing the database.
+  sqlite3_finalize sqlite-check-result ;
+
+: sqlite-reset ( statement -- )
+  #! Reset a statement so it can be called again, possibly with
+  #! different parameters.
+  sqlite3_reset sqlite-check-result ;
+
+: column-count ( statement -- int )
+  #! Given a prepared statement, return the number of
+  #! columns in each row of the result set of that statement.
+  sqlite3_column_count ;
+
+: column-text ( statement index -- string )
+  #! Return the value of the given column, indexed
+  #! from zero, as a string.
+  sqlite3_column_text ;
+
+: step-complete? ( step-result -- bool )
+  #! Return true if the result of a sqlite3_step is
+  #! such that the iteration has completed (ie. it is
+  #! SQLITE_DONE). Throw an error if an error occurs. 
+  dup SQLITE_ROW =  [
+    drop f
+  ] [
+    dup SQLITE_DONE = [
+      drop t 
+    ] [
+      sqlite-check-result t
+    ] if
+  ] if ;
+
+: sqlite-each ( statement quot -- )    
+  #! Execute the SQL statement, and call the quotation for
+  #! each row returned from executing the statement with the
+  #! statement on the top of the stack.
+  over sqlite3_step step-complete? [ 
+    2drop
+  ] [
+    [ call ] 2keep sqlite-each
+  ] if ; inline
+
+! For comparison, here is the linrec implementation of sqlite-each
+! [ drop sqlite3_step step-complete? ]
+! [ 2drop ]
+! [ 2dup 2slip ]
+! [ ] linrec ; 
+
+DEFER: (sqlite-map)
+
+: (sqlite-map) ( statement quot seq -- )    
+  pick sqlite3_step step-complete? [ 
+    2nip
+  ] [
+    >r 2dup call r> curry (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/libs/sql/tupledb.factor b/libs/sql/tupledb.factor
new file mode 100644 (file)
index 0000000..67276e1
--- /dev/null
@@ -0,0 +1,14 @@
+USING: kernel math sql:utils ;
+IN: sql
+
+: save ( tuple -- )
+    dup "id" tuple-slot [
+        ! update
+    ] [
+        ! insert
+    ] if ;
+
+: restore ( tuple -- )
+    ;
+
+
diff --git a/libs/sql/utils.factor b/libs/sql/utils.factor
new file mode 100644 (file)
index 0000000..dd7efcf
--- /dev/null
@@ -0,0 +1,151 @@
+USING: arrays errors generic hashtables kernel math namespaces\r
+prettyprint sequences sql strings tools words ;\r
+IN: sql:utils\r
+\r
+! : 2seq>hash 2array flip alist>hash ;\r
+\r
+: 2seq>hash ( seq seq -- hash )\r
+    H{ } clone -rot [ pick set-hash ] 2each ;\r
+\r
+: tuple-fields ( tuple -- seq )\r
+    class "slot-names" word-prop ;\r
+\r
+: tuple>parts ( tuple -- values names )\r
+    [ tuple-slots ] keep tuple-fields ;\r
+\r
+: tuple>hash ( tuple -- hash )\r
+    tuple>parts 2seq>hash ;\r
+\r
+: tuple>all-slots\r
+    delegates <reversed> V{ } clone\r
+    [ tuple-slots dupd nappend ] reduce\r
+    <reversed> prune <reversed> >array ;\r
+\r
+: tuple>all-fields\r
+    delegates <reversed> V{ } clone\r
+    [ tuple-fields dupd nappend ] reduce\r
+    <reversed> prune <reversed> >array ;\r
+    \r
+: full-tuple>hash ( tuple -- hash )\r
+    delegates <reversed>\r
+    H{ } clone [ tuple>hash hash-union ] reduce ;\r
+\r
+: tuple>all-parts ( tuple -- values names )\r
+    [\r
+        [ full-tuple>hash ] keep tuple>all-fields\r
+        [ swap hash ] map-with\r
+    ] keep tuple>all-fields ;\r
+\r
+: maybe-unparse ( obj -- )\r
+    dup string? [ unparse ] unless ;\r
+\r
+: replace ( new old seq -- seq )\r
+    >r 2seq>hash r> [\r
+        [\r
+            [\r
+                tuck swap hash* [ nip ] [ drop ] if\r
+                dup sequence? [ % ] [ , ] if \r
+            ] each-with\r
+        ] { } make\r
+    ] keep like ;\r
+\r
+GENERIC: escape-sql* ( string type db -- string )\r
+\r
+M: connection escape-sql* ( string type db -- string )\r
+    drop { "''" } "'" rot replace ;\r
+\r
+: escape-sql ( string type -- string ) db get escape-sql* ;\r
+\r
+: sanitize-name ( string -- string )\r
+    "_p" "-?" pick subst ;\r
+\r
+: tuple>sql-name ( tuple -- string )\r
+    class unparse sanitize-name ;\r
+\r
+: enquote% "'" % % "'" % ;\r
+\r
+: enquote ( string -- 'string' )\r
+    [ enquote% ] "" make ;\r
+\r
+: split-last ( seq -- last most )\r
+    dup length {\r
+        { [ dup zero? ] [ 2drop f f ] }\r
+        { [ dup 1 = ] [ drop f ] }\r
+        { [ t ] [ >r [ peek 1array ] keep r> 1- head ] }\r
+    } cond ;\r
+\r
+: (each-last) ( seq quot quot -- )\r
+    >r >r split-last r> each r> each ; inline\r
+\r
+: each-last ( seq quot quot -- )\r
+    >r dup clone r> append swap (each-last) ;\r
+\r
+: (2each-last) ( seq seq quot quot -- )\r
+    >r >r [ split-last ] 2apply swapd r> 2each r> 2each ; inline\r
+\r
+: 2each-last ( seq seq quot quot -- )\r
+    #! apply first quotation on all but last elt of seq\r
+    #! apply second quotation on last element\r
+    >r dup clone r> append swap (2each-last) ;\r
+\r
+! <foo1> { integer string }\r
+! mapping: { integer { varchar(256) "not null" } }\r
+! { "a integer" "b string" }\r
+\r
+SYMBOL: mappings\r
+H{ } clone mappings set-global\r
+\r
+: get-mapping ( tuple -- seq )\r
+    dup class mappings get hash* [\r
+        nip\r
+    ] [\r
+        drop tuple-slots [ drop "varchar" ] map\r
+    ] if ;\r
+\r
+: tuple>mapping% ( obj -- seq )\r
+    [ get-mapping ] keep tuple-fields\r
+    [ sanitize-name % " " % % ] [ ", " % ] 2each-last ;\r
+\r
+: tuple>mapping ( tuple -- string )\r
+    [ tuple>mapping% ] "" make ;\r
+\r
+: tuple>insert-parts ( tuple -- string )\r
+    [\r
+        tuple>parts\r
+        [\r
+            dup "id" = [\r
+                2drop\r
+            ] [\r
+                over [ swap 2array , ] [ 2drop ] if\r
+            ] if\r
+        ] 2each\r
+    ] { } make flip ;\r
+\r
+: tuple>assignments% ( tuple -- string )\r
+    [ tuple-slots [ maybe-unparse escape-sql ] map ] keep\r
+    tuple-fields\r
+    [ sanitize-name % " = " % enquote% ] [ ", " % ] 2each-last ;\r
+\r
+: tuple>assignments% ( tuple -- string )\r
+    tuple>parts dup [ "id" = ] find drop\r
+    dup -1 = [ "tuple must have an id slot" throw ] when\r
+    swap >r tuck >r remove-nth r> r> remove-nth\r
+    >r [ maybe-unparse escape-sql ] map r>\r
+    [ % " = " % enquote% ] [ ", " % ] 2each-last ;\r
+\r
+: tuple>assignments ( tuple -- string )\r
+    [ tuple>assignments% ] "" make ;\r
+\r
+: tuple-slot ( string slot -- ? obj )\r
+    "slot-names" over class word-props hash\r
+    rot [ = ] curry find over -1 = [\r
+        swap\r
+    ] [\r
+        drop 2 + swap tuple>array nth >r t r>\r
+    ] if ;\r
+\r
+: explode-tuple ( tuple -- )\r
+    dup tuple-slots swap class "slot-names" word-prop\r
+    [ set ] 2each ;\r
+\r
+\r