]> gitweb.factorcode.org Git - factor.git/commitdiff
db2: Update latest code from db4 branch. on to db5!
authorDoug Coleman <doug.coleman@gmail.com>
Wed, 19 Jan 2022 01:07:00 +0000 (19:07 -0600)
committerDoug Coleman <doug.coleman@gmail.com>
Wed, 19 Jan 2022 01:07:00 +0000 (19:07 -0600)
125 files changed:
basis/db2/binders/authors.txt [new file with mode: 0644]
basis/db2/binders/binders.factor [new file with mode: 0644]
basis/db2/connections/connections-tests.factor [new file with mode: 0644]
basis/db2/connections/connections.factor [new file with mode: 0644]
basis/db2/db2.factor [new file with mode: 0644]
basis/db2/debug/authors.txt [new file with mode: 0644]
basis/db2/debug/debug.factor [new file with mode: 0644]
basis/db2/errors/authors.txt [new file with mode: 0644]
basis/db2/errors/errors.factor [new file with mode: 0644]
basis/db2/introspection/authors.txt [new file with mode: 0644]
basis/db2/introspection/introspection.factor [new file with mode: 0644]
basis/db2/pools/authors.txt [new file with mode: 0644]
basis/db2/pools/pools-tests.factor [new file with mode: 0644]
basis/db2/pools/pools.factor [new file with mode: 0644]
basis/db2/queries/authors.txt [new file with mode: 0644]
basis/db2/queries/queries-tests.factor [new file with mode: 0644]
basis/db2/queries/queries.factor [new file with mode: 0644]
basis/db2/query-objects/authors.txt [new file with mode: 0644]
basis/db2/query-objects/query-objects-tests.factor [new file with mode: 0644]
basis/db2/query-objects/query-objects.factor [new file with mode: 0644]
basis/db2/result-sets/authors.txt [new file with mode: 0644]
basis/db2/result-sets/result-sets.factor [new file with mode: 0644]
basis/db2/statements/authors.txt [new file with mode: 0644]
basis/db2/statements/statements.factor [new file with mode: 0644]
basis/db2/statements/tests/tests.factor [new file with mode: 0644]
basis/db2/tester/tester.factor [new file with mode: 0644]
basis/db2/transactions/authors.txt [new file with mode: 0644]
basis/db2/transactions/transactions.factor [new file with mode: 0644]
basis/db2/types/authors.txt [new file with mode: 0644]
basis/db2/types/types.factor [new file with mode: 0644]
basis/db2/utils/authors.txt [new file with mode: 0644]
basis/db2/utils/utils.factor [new file with mode: 0644]
basis/mysql/authors.txt [new file with mode: 0644]
basis/mysql/db2/authors.txt [new file with mode: 0644]
basis/mysql/db2/connections/authors.txt [new file with mode: 0644]
basis/mysql/db2/connections/connections.factor [new file with mode: 0644]
basis/mysql/db2/db2.factor [new file with mode: 0644]
basis/mysql/db2/ffi/authors.txt [new file with mode: 0644]
basis/mysql/db2/ffi/ffi.factor [new file with mode: 0644]
basis/mysql/db2/lib/authors.txt [new file with mode: 0644]
basis/mysql/db2/lib/lib.factor [new file with mode: 0644]
basis/mysql/db2/result-sets/authors.txt [new file with mode: 0644]
basis/mysql/db2/result-sets/result-sets.factor [new file with mode: 0644]
basis/mysql/db2/statements/authors.txt [new file with mode: 0644]
basis/mysql/db2/statements/statements.factor [new file with mode: 0644]
basis/mysql/mysql.factor [new file with mode: 0644]
basis/mysql/orm/authors.txt [new file with mode: 0644]
basis/mysql/orm/orm.factor [new file with mode: 0644]
basis/orm/authors.txt [new file with mode: 0644]
basis/orm/binders/authors.txt [new file with mode: 0644]
basis/orm/binders/binders.factor [new file with mode: 0644]
basis/orm/examples/authors.txt [new file with mode: 0644]
basis/orm/examples/examples.factor [new file with mode: 0644]
basis/orm/orm.factor [new file with mode: 0644]
basis/orm/persistent/authors.txt [new file with mode: 0644]
basis/orm/persistent/persistent-tests.factor [new file with mode: 0644]
basis/orm/persistent/persistent.factor [new file with mode: 0644]
basis/orm/queries/authors.txt [new file with mode: 0644]
basis/orm/queries/queries-tests.factor [new file with mode: 0644]
basis/orm/queries/queries.factor [new file with mode: 0644]
basis/orm/query-objects/authors.txt [new file with mode: 0644]
basis/orm/query-objects/query-objects.factor [new file with mode: 0644]
basis/orm/tuples/authors.txt [new file with mode: 0644]
basis/orm/tuples/tuples-tests.factor [new file with mode: 0644]
basis/orm/tuples/tuples.factor [new file with mode: 0644]
basis/postgresql/authors.txt [new file with mode: 0644]
basis/postgresql/db2/authors.txt [new file with mode: 0644]
basis/postgresql/db2/connections/authors.txt [new file with mode: 0644]
basis/postgresql/db2/connections/connections-tests.factor [new file with mode: 0644]
basis/postgresql/db2/connections/connections.factor [new file with mode: 0644]
basis/postgresql/db2/db2.factor [new file with mode: 0644]
basis/postgresql/db2/errors/authors.txt [new file with mode: 0644]
basis/postgresql/db2/errors/errors.factor [new file with mode: 0644]
basis/postgresql/db2/ffi/authors.txt [new file with mode: 0644]
basis/postgresql/db2/ffi/ffi.factor [new file with mode: 0644]
basis/postgresql/db2/introspection/authors.txt [new file with mode: 0644]
basis/postgresql/db2/introspection/introspection.factor [new file with mode: 0644]
basis/postgresql/db2/lib/authors.txt [new file with mode: 0644]
basis/postgresql/db2/lib/lib.factor [new file with mode: 0644]
basis/postgresql/db2/queries/authors.txt [new file with mode: 0644]
basis/postgresql/db2/queries/queries.factor [new file with mode: 0644]
basis/postgresql/db2/result-sets/authors.txt [new file with mode: 0644]
basis/postgresql/db2/result-sets/result-sets.factor [new file with mode: 0644]
basis/postgresql/db2/statements/authors.txt [new file with mode: 0644]
basis/postgresql/db2/statements/statements.factor [new file with mode: 0644]
basis/postgresql/db2/tests/authors.txt [new file with mode: 0644]
basis/postgresql/db2/tests/tests.factor [new file with mode: 0644]
basis/postgresql/db2/types/authors.txt [new file with mode: 0644]
basis/postgresql/db2/types/types.factor [new file with mode: 0644]
basis/postgresql/orm/authors.txt [new file with mode: 0644]
basis/postgresql/orm/orm.factor [new file with mode: 0644]
basis/postgresql/orm/queries/authors.txt [new file with mode: 0644]
basis/postgresql/orm/queries/queries.factor [new file with mode: 0644]
basis/postgresql/postgresql.factor [new file with mode: 0644]
basis/reconstructors/authors.txt [new file with mode: 0644]
basis/reconstructors/reconstructors.factor [new file with mode: 0644]
basis/sqlite/authors.txt [new file with mode: 0644]
basis/sqlite/db2/authors.txt [new file with mode: 0644]
basis/sqlite/db2/connections/authors.txt [new file with mode: 0644]
basis/sqlite/db2/connections/connections-tests.factor [new file with mode: 0644]
basis/sqlite/db2/connections/connections.factor [new file with mode: 0644]
basis/sqlite/db2/db2.factor [new file with mode: 0644]
basis/sqlite/db2/errors/authors.txt [new file with mode: 0644]
basis/sqlite/db2/errors/errors.factor [new file with mode: 0644]
basis/sqlite/db2/ffi/ffi.factor [new file with mode: 0644]
basis/sqlite/db2/introspection/authors.txt [new file with mode: 0644]
basis/sqlite/db2/introspection/introspection.factor [new file with mode: 0644]
basis/sqlite/db2/lib/lib.factor [new file with mode: 0644]
basis/sqlite/db2/queries/authors.txt [new file with mode: 0644]
basis/sqlite/db2/queries/queries.factor [new file with mode: 0644]
basis/sqlite/db2/result-sets/authors.txt [new file with mode: 0644]
basis/sqlite/db2/result-sets/result-sets.factor [new file with mode: 0644]
basis/sqlite/db2/statements/authors.txt [new file with mode: 0644]
basis/sqlite/db2/statements/statements.factor [new file with mode: 0644]
basis/sqlite/db2/tests/authors.txt [new file with mode: 0644]
basis/sqlite/db2/tests/tests.factor [new file with mode: 0644]
basis/sqlite/db2/types/authors.txt [new file with mode: 0644]
basis/sqlite/db2/types/types.factor [new file with mode: 0644]
basis/sqlite/orm/authors.txt [new file with mode: 0644]
basis/sqlite/orm/orm.factor [new file with mode: 0644]
basis/sqlite/orm/queries/authors.txt [new file with mode: 0644]
basis/sqlite/orm/queries/queries.factor [new file with mode: 0644]
basis/sqlite/orm/types/authors.txt [new file with mode: 0644]
basis/sqlite/orm/types/types.factor [new file with mode: 0644]
basis/sqlite/sqlite.factor [new file with mode: 0644]

diff --git a/basis/db2/binders/authors.txt b/basis/db2/binders/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/db2/binders/binders.factor b/basis/db2/binders/binders.factor
new file mode 100644 (file)
index 0000000..7107a0e
--- /dev/null
@@ -0,0 +1,89 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors classes.tuple constructors db2.types db2.utils
+kernel math math.parser multiline parser quotations sequences ;
+IN: db2.binders
+
+TUPLE: table-ordinal table-name table-ordinal ;
+TUPLE: table-ordinal-column < table-ordinal column-name ;
+CONSTRUCTOR: <table-ordinal> table-ordinal
+    ( table-name table-ordinal -- obj ) ;
+CONSTRUCTOR: <table-ordinal-column> table-ordinal-column
+    ( table-name table-ordinal column-name -- obj ) ;
+
+SYNTAX: TO{
+    \ } [ 2 ensure-length first2 <table-ordinal> ] parse-literal ;
+
+SYNTAX: TOC{
+    \ } [ 3 ensure-length first3 <table-ordinal-column> ] parse-literal ;
+
+TUPLE: binder ;
+TUPLE: low-binder value type ;
+TUPLE: high-binder < low-binder class toc ;
+
+TUPLE: in-binder-low < low-binder ;
+CONSTRUCTOR: <in-binder-low> in-binder-low ( value type -- obj ) ;
+TUPLE: in-binder < high-binder ;
+CONSTRUCTOR: <in-binder> in-binder ( -- obj ) ;
+
+SYNTAX: TYPED{
+    \ } [ first2 <in-binder-low> ] parse-literal ;
+
+TUPLE: out-binder-low < binder type ;
+CONSTRUCTOR: <out-binder-low> out-binder-low ( type -- obj ) ;
+TUPLE: out-binder < high-binder ;
+CONSTRUCTOR: <out-binder> out-binder ( toc type -- obj ) ;
+
+TUPLE: and-binder binders ;
+TUPLE: or-binder binders ;
+
+TUPLE: join-binder < binder toc1 toc2 ;
+CONSTRUCTOR: <join-binder> join-binder ( toc1 toc2 -- obj ) ;
+
+TUPLE: count-function < out-binder ;
+CONSTRUCTOR: <count-function> count-function ( toc -- obj )
+    INTEGER >>type ;
+
+TUPLE: sum-function < out-binder ;
+CONSTRUCTOR: <sum-function> sum-function ( toc -- obj )
+    REAL >>type ;
+
+TUPLE: average-function < out-binder ;
+CONSTRUCTOR: <average-function> average-function ( toc -- obj )
+    REAL >>type ;
+
+TUPLE: min-function < out-binder ;
+CONSTRUCTOR: <min-function> min-function ( toc -- obj )
+    REAL >>type ;
+
+TUPLE: max-function < out-binder ;
+CONSTRUCTOR: <max-function> max-function ( toc -- obj )
+    REAL >>type ;
+
+TUPLE: first-function < out-binder ;
+CONSTRUCTOR: <first-function> first-function ( toc -- obj )
+    REAL >>type ;
+
+TUPLE: last-function < out-binder ;
+CONSTRUCTOR: <last-function> last-function ( toc -- obj )
+    REAL >>type ;
+
+TUPLE: equal-binder < in-binder ;
+CONSTRUCTOR: <equal-binder> equal-binder ( -- obj ) ;
+TUPLE: not-equal-binder < in-binder ;
+CONSTRUCTOR: <not-equal-binder> not-equal-binder ( -- obj ) ;
+TUPLE: less-than-binder < in-binder ;
+CONSTRUCTOR: <less-than-binder> less-than-binder ( -- obj ) ;
+TUPLE: less-than-equal-binder < in-binder ;
+CONSTRUCTOR: <less-than-equal-binder> less-than-equal-binder ( -- obj ) ;
+TUPLE: greater-than-binder < in-binder ;
+CONSTRUCTOR: <greater-than-binder> greater-than-binder ( -- obj ) ;
+TUPLE: greater-than-equal-binder < in-binder ;
+CONSTRUCTOR: <greater-than-equal-binder> greater-than-equal-binder ( -- obj ) ;
+
+TUPLE: relation-binder
+class1 toc1 column1
+class2 toc2 column2
+relation-type ;
+
+CONSTRUCTOR: <relation-binder> relation-binder ( class1 toc1 column1 class2 toc2 column2 relation-type -- obj ) ;
diff --git a/basis/db2/connections/connections-tests.factor b/basis/db2/connections/connections-tests.factor
new file mode 100644 (file)
index 0000000..bf1ba97
--- /dev/null
@@ -0,0 +1,8 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: tools.test db2.connections db2.debug ;
+IN: db2.connections.tests
+
+! Tests connection
+
+{ 1 0 } [ [ ] with-db ] must-infer-as
diff --git a/basis/db2/connections/connections.factor b/basis/db2/connections/connections.factor
new file mode 100644 (file)
index 0000000..938b76c
--- /dev/null
@@ -0,0 +1,19 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors destructors fry kernel namespaces ;
+IN: db2.connections
+
+TUPLE: db-connection < disposable handle db ;
+
+: new-db-connection ( handle class -- db-connection )
+    new-disposable
+        swap >>handle ; inline
+
+GENERIC: db>db-connection-generic ( db -- db-connection )
+
+: db>db-connection ( db -- db-connection )
+    [ db>db-connection-generic ] keep >>db ; inline
+
+: with-db ( db quot -- )
+    [ db>db-connection db-connection over ] dip
+    '[ _ [ drop @ ] with-disposal ] with-variable ; inline
diff --git a/basis/db2/db2.factor b/basis/db2/db2.factor
new file mode 100644 (file)
index 0000000..a0df8b6
--- /dev/null
@@ -0,0 +1,75 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors continuations db2.query-objects db2.result-sets
+db2.statements destructors fry kernel locals math reconstructors
+sequences strings summary vocabs.loader vocabs ;
+IN: db2
+
+ERROR: no-in-types statement ;
+ERROR: no-out-types statement ;
+
+GENERIC: sql-command ( object -- )
+GENERIC: sql-query ( object -- sequence )
+
+M: string sql-command ( string -- )
+    <statement>
+        swap >>sql
+    sql-command ;
+
+M: string sql-query ( string -- sequence )
+    <statement>
+        swap >>sql
+    sql-query ;
+
+ERROR: retryable-failed statement ;
+
+: execute-retry-quotation ( statement -- statement )
+    dup retry-quotation>> call( statement -- statement ) ;
+
+:: (run-retryable) ( statement quot: ( statement -- statement ) -- obj )
+    statement retries>> 0 > [
+        statement [ 1 - ] change-retries drop
+        [
+            statement quot call
+        ] [
+            statement errors>> push
+            statement execute-retry-quotation reset-statement
+            quot (run-retryable)
+        ] recover
+    ] [
+        statement retryable-failed
+    ] if ; inline recursive
+
+: run-retryable ( statement quot -- )
+    over retries>> [
+        '[ _ (run-retryable) ] with-disposal
+    ] [
+        with-disposal
+    ] if ; inline
+
+M: statement sql-command ( statement -- )
+    [
+        prepare-statement
+        [ bind-sequence ] [ statement>result-set ] bi
+    ] run-retryable drop ; inline
+
+M: query sql-command
+    query-object>statement sql-command ;
+
+M: statement sql-query ( statement -- sequence )
+    [
+        [
+            prepare-statement
+            [ bind-sequence ] [ statement>result-sequence ] bi
+        ] [
+            reconstructor>> [ call( obj -- obj ) ] when*
+        ] bi
+    ] run-retryable ; inline
+
+M: sequence sql-command [ sql-command ] each ;
+M: sequence sql-query [ sql-query ] map ;
+M: query sql-query
+    query-object>statement sql-query ;
+
+"db2.queries" require
+"db2.transactions" require
diff --git a/basis/db2/debug/authors.txt b/basis/db2/debug/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/db2/debug/debug.factor b/basis/db2/debug/debug.factor
new file mode 100644 (file)
index 0000000..6ceca3f
--- /dev/null
@@ -0,0 +1,50 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors combinators db2.connections postgresql.db2
+sqlite.db2 fry io.files.temp kernel namespaces system tools.test ;
+IN: db2.debug
+
+: sqlite-test-db ( -- sqlite-db )
+    "tuples-test.db" temp-file <sqlite-db> ;
+
+! These words leak resources, but are useful for interactivel testing
+: set-sqlite-db ( -- )
+    sqlite-db db>db-connection db-connection set ;
+
+: test-sqlite-quot ( quot -- quot' )
+    '[ sqlite-test-db _ with-db ] ; inline
+
+: test-sqlite ( quot -- ) test-sqlite-quot call ; inline
+: test-sqlite0 ( quot -- ) test-sqlite-quot call( -- ) ; inline
+
+: postgresql-test-db ( -- postgresql-db )
+    <postgresql-db>
+        "localhost" >>host
+        "erg" >>username
+        "thepasswordistrust" >>password
+        "factor-test" >>database ;
+
+: set-postgresql-db ( -- )
+    postgresql-db db>db-connection db-connection set ;
+
+: test-postgresql-quot ( quot -- quot' )
+    '[
+        os windows? cpu x86.64? and [
+            [ ] [ postgresql-test-db _ with-db ] unit-test
+        ] unless
+    ] ; inline
+
+: test-postgresql ( quot -- ) test-postgresql-quot call ; inline
+: test-postgresql0 ( quot -- ) test-postgresql-quot call( -- ) ; inline
+
+: test-dbs ( quot -- )
+    {
+        [ test-sqlite0 ]
+        [ test-postgresql0 ]
+    } cleave ;
+
+: with-dummy-postgresql ( quot -- )
+    [ postgresql-test-db ] dip with-db ; inline
+
+: with-dummy-sqlite ( quot -- )
+    [ sqlite-test-db ] dip with-db ; inline
diff --git a/basis/db2/errors/authors.txt b/basis/db2/errors/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/db2/errors/errors.factor b/basis/db2/errors/errors.factor
new file mode 100644 (file)
index 0000000..788e068
--- /dev/null
@@ -0,0 +1,42 @@
+! Copyright (C) 2008 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: constructors continuations db2.connections fry kernel ;
+IN: db2.errors
+
+ERROR: db-error ;
+
+TUPLE: sql-error location ;
+HOOK: parse-sql-error db-connection ( error -- error' )
+
+TUPLE: sql-unknown-error < sql-error message ;
+CONSTRUCTOR: <sql-unknown-error> sql-unknown-error ( message -- error ) ;
+
+TUPLE: sql-table-exists < sql-error table ;
+CONSTRUCTOR: <sql-table-exists> sql-table-exists ( table -- error ) ;
+
+TUPLE: sql-table-missing < sql-error table ;
+CONSTRUCTOR: <sql-table-missing> sql-table-missing ( table -- error ) ;
+
+TUPLE: sql-syntax-error < sql-error message ;
+CONSTRUCTOR: <sql-syntax-error> sql-syntax-error ( message -- error ) ;
+
+TUPLE: sql-function-exists < sql-error message ;
+CONSTRUCTOR: <sql-function-exists> sql-function-exists ( message -- error ) ;
+
+TUPLE: sql-function-missing < sql-error message ;
+CONSTRUCTOR: <sql-function-missing> sql-function-missing ( message -- error ) ;
+
+: ignore-error ( quot word -- )
+    '[ dup _ execute [ drop ] [ rethrow ] if ] recover ; inline
+
+: ignore-table-exists ( quot -- )
+    \ sql-table-exists? ignore-error ; inline
+
+: ignore-table-missing ( quot -- )
+    \ sql-table-missing? ignore-error ; inline
+
+: ignore-function-exists ( quot -- )
+    \ sql-function-exists? ignore-error ; inline
+
+: ignore-function-missing ( quot -- )
+    \ sql-function-missing? ignore-error ; inline
diff --git a/basis/db2/introspection/authors.txt b/basis/db2/introspection/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/db2/introspection/introspection.factor b/basis/db2/introspection/introspection.factor
new file mode 100644 (file)
index 0000000..0d93003
--- /dev/null
@@ -0,0 +1,13 @@
+! Copyright (C) 2011 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: db2.connections ;
+IN: db2.introspection
+
+HOOK: all-db-objects db-connection ( -- sequence )
+HOOK: all-tables db-connection ( -- sequence )
+HOOK: all-indices db-connection ( -- sequence )
+HOOK: temporary-db-objects db-connection ( -- sequence )
+
+HOOK: table-columns db-connection ( name -- sequence )
+
+
diff --git a/basis/db2/pools/authors.txt b/basis/db2/pools/authors.txt
new file mode 100644 (file)
index 0000000..1901f27
--- /dev/null
@@ -0,0 +1 @@
+Slava Pestov
diff --git a/basis/db2/pools/pools-tests.factor b/basis/db2/pools/pools-tests.factor
new file mode 100644 (file)
index 0000000..1012159
--- /dev/null
@@ -0,0 +1,20 @@
+USING: accessors continuations db2.pools sqlite.db2
+sqlite.db2.connections destructors io.directories io.files
+io.files.temp kernel math namespaces tools.test ;
+IN: db2.pools.tests
+
+{ 1 0 } [ [ ] with-db-pool ] must-infer-as
+
+{ 1 0 } [ [ ] with-pooled-db ] must-infer-as
+
+! Test behavior after image save/load
+
+[ "pool-test.db" temp-file delete-file ] ignore-errors
+
+[ ] [ "pool-test.db" temp-file <sqlite-db> <db-pool> "pool" set ] unit-test
+
+[ ] [ "pool" get expired>> t >>expired drop ] unit-test
+
+[ ] [ 1000 [ "pool" get [ ] with-pooled-db ] times ] unit-test
+
+[ ] [ "pool" get dispose ] unit-test
diff --git a/basis/db2/pools/pools.factor b/basis/db2/pools/pools.factor
new file mode 100644 (file)
index 0000000..817c448
--- /dev/null
@@ -0,0 +1,19 @@
+! Copyright (C) 2008 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors db2.connections fry io.pools kernel namespaces ;
+IN: db2.pools
+
+TUPLE: db-pool < pool db ;
+
+: <db-pool> ( db -- pool )
+    db-pool <pool>
+        swap >>db ; inline
+
+: with-db-pool ( db quot -- )
+    [ <db-pool> ] dip with-pool ; inline
+
+M: db-pool make-connection ( pool -- connection )
+    db>> db>db-connection ;
+
+: with-pooled-db ( pool quot -- )
+    '[ db-connection _ with-variable ] with-pooled-connection ; inline
diff --git a/basis/db2/queries/authors.txt b/basis/db2/queries/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/db2/queries/queries-tests.factor b/basis/db2/queries/queries-tests.factor
new file mode 100644 (file)
index 0000000..214545c
--- /dev/null
@@ -0,0 +1,17 @@
+! Copyright (C) 2010 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: db2 db2.debug db2.queries debugger kernel sequences
+tools.test ;
+IN: db2.queries.tests
+
+: test-table-exists ( -- )
+    [ "drop table table_omg;" sql-command ] try
+    [ f ] [ "table_omg" table-exists? ] unit-test
+    [ ] [ "create table table_omg(id integer);" sql-command ] unit-test
+    [ t ] [ "table_omg" table-exists? ] unit-test
+    [ t ] [ "default_person" table-columns empty? not ] unit-test
+
+    [ ] [ "factor-test" database-tables drop ] unit-test
+    [ ] [ databases drop ] unit-test ;
+
+[ test-table-exists ] test-dbs
diff --git a/basis/db2/queries/queries.factor b/basis/db2/queries/queries.factor
new file mode 100644 (file)
index 0000000..6202a0e
--- /dev/null
@@ -0,0 +1,103 @@
+! Copyright (C) 2010 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors arrays ascii classes.tuple
+combinators.short-circuit db2 db2.connections db2.statements
+db2.types db2.utils fry kernel sequences strings ;
+IN: db2.queries
+
+TUPLE: sql-object ;
+TUPLE: sql-column ;
+
+HOOK: current-db-name db-connection ( -- string )
+HOOK: sanitize-string db-connection ( string -- string )
+
+HOOK: databases-statement db-connection ( -- statement )
+HOOK: database-tables-statement db-connection ( database -- statement )
+HOOK: database-table-columns-statement db-connection ( database table -- sequence )
+
+HOOK: sql-object-class db-connection ( -- tuple-class )
+HOOK: sql-column-class db-connection ( -- tuple-class )
+
+ERROR: unsafe-sql-string string ;
+
+M: object sanitize-string
+    dup [ { [ Letter? ] [ digit? ] [ "_" member? ] } 1|| ] all?
+    [ unsafe-sql-string ] unless ;
+
+<PRIVATE
+GENERIC: >sql-name* ( object -- string )
+M: tuple-class >sql-name* name>> sql-name-replace ;
+M: string >sql-name* sql-name-replace ;
+PRIVATE>
+
+: >sql-name ( object -- string ) >sql-name* sanitize-string ;
+
+: information-schema-select-sql ( string -- string' )
+    "SELECT * FROM information_schema." " " surround ;
+
+: database-table-schema-select-sql ( string -- string )
+    information-schema-select-sql
+    "WHERE
+            table_catalog=$1 AND
+            table_name=$2 AND
+            table_schema='public'" append ;
+
+: database-schema-select-sql ( string -- string )
+    information-schema-select-sql
+    "WHERE
+            table_catalog=$1 AND
+            table_schema='public'" append ;
+
+M: object database-tables-statement
+    [ <statement> ] dip
+        1array >>in
+        "tables" database-schema-select-sql >>sql ;
+
+M: object databases-statement
+    <statement>
+        "SELECT DISTINCT table_catalog
+        FROM information_schema.tables
+        WHERE
+            table_schema='public'" >>sql ;
+
+M: object database-table-columns-statement ( database table -- sequence )
+    [ <statement> ] 2dip
+        2array >>in
+        "columns" database-table-schema-select-sql >>sql ;
+
+: >sql-objects ( statement -- sequence' )
+    sql-query
+    sql-object-class '[ _ slots>tuple ] map ;
+
+: >sql-columns ( statement -- sequence' )
+    sql-query
+    sql-column-class '[ _ slots>tuple ] map ;
+
+: database-tables ( database -- sequence )
+    database-tables-statement >sql-objects ;
+
+: current-tables ( -- sequence )
+    current-db-name database-tables ;
+
+: table-names ( sequence -- strings )
+    [ table-name>> ] map ;
+
+: database-table-names ( database -- sequence )
+    database-tables table-names ;
+
+: current-table-names ( -- sequence )
+    current-db-name database-table-names ;
+
+: table-exists? ( table -- ? ) current-table-names member? ;
+
+: database-table-columns ( database table -- sequence )
+    database-table-columns-statement >sql-columns ;
+
+: table-columns ( table -- sequence )
+    [ current-db-name ] dip database-table-columns ;
+
+: databases ( -- sequence )
+    databases-statement sql-query concat ;
+
+! [ "select nspname from pg_catalog.pg_namespace" sql-query ] with-dummy-postgresql
+! [ "select schema_name from information_schema.schemata" sql-query ] with-dummy-postgresql
diff --git a/basis/db2/query-objects/authors.txt b/basis/db2/query-objects/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/db2/query-objects/query-objects-tests.factor b/basis/db2/query-objects/query-objects-tests.factor
new file mode 100644 (file)
index 0000000..5d4a285
--- /dev/null
@@ -0,0 +1,545 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: db2.binders db2.connections
+postgresql.db2.connections
+postgresql.db2.connections.private db2.query-objects
+sqlite.db2.connections db2.statements db2.types namespaces
+tools.test ;
+IN: db2.query-objects.tests
+
+! TOC - table ordinal column
+
+! Test expansion of insert
+TUPLE: qdog id age ;
+
+! Test joins
+TUPLE: user id name ;
+TUPLE: address id user-id street city state zip ;
+
+[
+T{ statement
+    { sql "INSERT INTO qdog (id) VALUES(?);" }
+    { in
+        {
+            T{ in-binder
+                { toc TOC{ "qdog" "0" "id" } }
+                { type INTEGER }
+                { value 0 }
+            }
+        }
+    }
+    { out V{ } }
+    { errors V{ } }
+}
+] [
+    T{ sqlite-db-connection } db-connection [
+        T{ insert
+            { in
+                {
+                    T{ in-binder
+                        { toc TOC{ "qdog" "0" "id" } }
+                        { type INTEGER }
+                        { value 0 }
+                    }
+                }
+            }
+        } query-object>statement
+    ] with-variable
+] unit-test
+
+[
+T{ statement
+    { sql "INSERT INTO qdog (id) VALUES($1);" }
+    { in
+        {
+            T{ in-binder
+                { toc TOC{ "qdog" "0" "id" } }
+                { type INTEGER }
+                { value 0 }
+            }
+        }
+    }
+    { out V{ } }
+    { errors V{ } }
+}
+] [
+    T{ postgresql-db-connection } db-connection
+    [
+        T{ insert
+            { in
+                {
+                    T{ in-binder
+                        { toc TOC{ "qdog" "0" "id" } }
+                        { type INTEGER }
+                        { value 0 }
+                    }
+                }
+            }
+        } query-object>statement
+    ] with-variable
+] unit-test
+
+
+
+
+
+
+[
+T{ statement
+    { sql "SELECT qdog0.id, qdog0.age FROM qdog AS qdog0 WHERE qdog0.age = ?;" }
+    { in
+        {
+            T{ equal-binder
+                { toc TOC{ "qdog" "0" "age" } }
+                { type INTEGER }
+                { value 0 }
+            }
+        }
+    }
+    { out
+        {
+            T{ out-binder
+                { toc TOC{ "qdog" "0" "id" } }
+                { type INTEGER }
+            }
+            T{ out-binder
+                { toc TOC{ "qdog" "0" "age" } }
+                { type INTEGER }
+            }
+        }
+    }
+    { errors V{ } }
+}
+] [
+    T{ sqlite-db-connection } db-connection
+    [
+        T{ select
+            { from { TO{ "qdog" "0" } } }
+            { out
+                {
+                    T{ out-binder
+                        { toc TOC{ "qdog" "0" "id" } }
+                        { type INTEGER }
+                    }
+                    T{ out-binder
+                        { toc TOC{ "qdog" "0" "age" } }
+                        { type INTEGER }
+                    }
+                }
+            }
+            { in
+                {
+                    T{ equal-binder
+                        { toc TOC{ "qdog" "0" "age" } }
+                        { type INTEGER }
+                        { value 0 }
+                    }
+                }
+            }
+        } query-object>statement
+    ] with-variable
+] unit-test
+
+
+
+
+[
+T{ statement
+    { sql "UPDATE qdog SET age = ? WHERE age = ?;" }
+    { in
+        {
+            T{ equal-binder
+                { toc TOC{ "qdog" "0" "age" } }
+                { type INTEGER }
+                { value 1 }
+            }
+            T{ equal-binder
+                { toc TOC{ "qdog" "0" "age" } }
+                { type INTEGER }
+                { value 0 }
+            }
+        }
+    }
+    { out V{ } }
+    { errors V{ } }
+}
+] [
+    T{ sqlite-db-connection } db-connection
+    [
+        T{ update
+            { in
+                {
+                    T{ equal-binder
+                        { toc TOC{ "qdog" "0" "age" } }
+                        { type INTEGER }
+                        { value 1 }
+                    }
+                }
+            }
+            { where
+                {
+                    T{ equal-binder
+                        { toc TOC{ "qdog" "0" "age" } }
+                        { type INTEGER }
+                        { value 0 }
+                    }
+                }
+            }
+        } query-object>statement
+    ] with-variable
+] unit-test
+
+[
+T{ statement
+    { sql "DELETE FROM qdog WHERE age = ?;" }
+    { in
+        {
+            T{ equal-binder
+                { toc TOC{ "qdog" "0" "age" } }
+                { type INTEGER }
+                { value 0 }
+            }
+        }
+    }
+    { out V{ } }
+    { errors V{ } }
+}
+] [
+    T{ sqlite-db-connection } db-connection
+    [
+        T{ delete
+            { where
+                {
+                    T{ equal-binder
+                        { toc TOC{ "qdog" "0" "age" } }
+                        { type INTEGER }
+                        { value 0 }
+                    }
+                }
+            }
+        } query-object>statement
+    ] with-variable
+] unit-test
+
+
+
+[
+T{ statement
+    { sql "SELECT COUNT(qdog0.id) FROM qdog AS qdog0;" }
+    { in { } }
+    { out
+        {
+            T{ count-function
+                { toc TOC{ "qdog" "0" "id" } }
+                { type INTEGER }
+            }
+        }
+    }
+    { errors V{ } }
+}
+] [
+    T{ sqlite-db-connection } db-connection
+    [
+        T{ select
+            { from { TO{ "qdog" "0" } } }
+            { out
+                {
+                    T{ count-function
+                        { toc TOC{ "qdog" "0" "id" } }
+                        { type INTEGER }
+                    }
+                }
+            }
+        } query-object>statement
+    ] with-variable
+] unit-test
+
+
+
+
+[
+T{ statement
+    { sql "SELECT COUNT(qdog0.id) FROM qdog AS qdog0 WHERE qdog0.age = ?;" }
+    { in
+        {
+            T{ equal-binder
+                { toc TOC{ "qdog" "0" "age" } }
+                { type INTEGER }
+                { value 0 }
+            }
+        }
+    }
+    { out
+        {
+            T{ count-function
+                { toc TOC{ "qdog" "0" "id" } }
+                { type INTEGER }
+            }
+        }
+    }
+    { errors V{ } }
+}
+] [
+    T{ sqlite-db-connection } db-connection
+    [
+        T{ select
+            { from { TO{ "qdog" "0" } } }
+            { out
+                {
+                    T{ count-function
+                        { toc TOC{ "qdog" "0" "id" } }
+                        { type INTEGER }
+                    }
+                }
+            }
+            { in
+                {
+                    T{ equal-binder
+                        { toc TOC{ "qdog" "0" "age" } }
+                        { type INTEGER }
+                        { value 0 }
+                    }
+                }
+            }
+        } query-object>statement
+    ] with-variable
+] unit-test
+
+
+[
+T{ statement
+    { sql
+        "SELECT user0.id, user0.name, address0.id, address0.user_id, address0.street, address0.city, address0.zip FROM user AS user0 LEFT JOIN address AS address0 ON user0.id = address0.user_id;"
+    }
+    { in { } }
+    { out
+        {
+            T{ out-binder
+                { toc TOC{ "user" "0" "id" } }
+                { type INTEGER }
+            }
+            T{ out-binder
+                { toc TOC{ "user" "0" "name" } }
+                { type VARCHAR }
+            }
+            T{ out-binder
+                { toc TOC{ "address" "0" "id" } }
+                { type INTEGER }
+            }
+            T{ out-binder
+                { toc TOC{ "address" "0" "user_id" } }
+                { type INTEGER }
+            }
+            T{ out-binder
+                { toc TOC{ "address" "0" "street" } }
+                { type VARCHAR }
+            }
+            T{ out-binder
+                { toc TOC{ "address" "0" "city" } }
+                { type VARCHAR }
+            }
+            T{ out-binder
+                { toc TOC{ "address" "0" "zip" } }
+                { type INTEGER }
+            }
+        }
+    }
+    { errors V{ } }
+}
+] [
+    T{ sqlite-db-connection } db-connection
+    [
+        T{ select
+            { out
+                {
+                    T{ out-binder
+                        { toc TOC{ "user" "0" "id" } }
+                        { type INTEGER }
+                    }
+                    T{ out-binder
+                        { toc TOC{ "user" "0" "name" } }
+                        { type VARCHAR }
+                    }
+                    T{ out-binder
+                        { toc TOC{ "address" "0" "id" } }
+                        { type INTEGER }
+                    }
+                    T{ out-binder
+                        { toc TOC{ "address" "0" "user_id" } }
+                        { type INTEGER }
+                    }
+                    T{ out-binder
+                        { toc TOC{ "address" "0" "street" } }
+                        { type VARCHAR }
+                    }
+                    T{ out-binder
+                        { toc TOC{ "address" "0" "city" } }
+                        { type VARCHAR }
+                    }
+                    T{ out-binder
+                        { toc TOC{ "address" "0" "zip" } }
+                        { type INTEGER }
+                    }
+                }
+            }
+            { from { TO{ "user" "0" } } }
+            { join
+                {
+                    T{ join-binder
+                        { toc1 TOC{ "user" "0" "id" } }
+                        { toc2 TOC{ "address" "0" "user_id" } }
+                    }
+                }
+            }
+        } query-object>statement
+    ] with-variable
+] unit-test
+
+
+[
+T{ statement
+    { sql
+        "SELECT user0.id, user0.name FROM user AS user0 WHERE (user0.id = ? AND user0.id = ?);"
+    }
+    { in
+        {
+            T{ equal-binder
+                { toc TOC{ "user" "0" "id" } }
+                { type INTEGER }
+                { value 0 }
+            }
+            T{ equal-binder
+                { toc TOC{ "user" "0" "id" } }
+                { type INTEGER }
+                { value 1 }
+            }
+        }
+    }
+    { out
+        {
+            T{ out-binder
+                { toc TOC{ "user" "0" "id" } }
+                { type INTEGER }
+            }
+            T{ out-binder
+                { toc TOC{ "user" "0" "name" } }
+                { type VARCHAR }
+            }
+        }
+    }
+    { errors V{ } }
+}
+] [
+    T{ sqlite-db-connection } db-connection
+    [
+        T{ select
+            { in
+                {
+                    T{ and-binder
+                        { binders
+                            {
+                                T{ equal-binder
+                                    { toc TOC{ "user" "0" "id" } }
+                                    { type INTEGER }
+                                    { value 0 }
+                                }
+                                T{ equal-binder
+                                    { toc TOC{ "user" "0" "id" } }
+                                    { type INTEGER }
+                                    { value 1 }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            { out
+                {
+                    T{ out-binder
+                        { toc TOC{ "user" "0" "id" } }
+                        { type INTEGER }
+                    }
+                    T{ out-binder
+                        { toc TOC{ "user" "0" "name" } }
+                        { type VARCHAR }
+                    }
+                }
+            }
+            { from { TO{ "user" "0" } } }
+        } query-object>statement
+    ] with-variable
+] unit-test
+
+[
+T{ statement
+    { sql
+        "SELECT user0.id, user0.name FROM user AS user0 WHERE (qdog0.id > ? AND qdog0.id <= ?);"
+    }
+    { in
+        {
+            T{ greater-than-binder
+                { toc TOC{ "qdog" "0" "id" } }
+                { type INTEGER }
+                { value 0 }
+            }
+            T{ less-than-equal-binder
+                { toc TOC{ "qdog" "0" "id" } }
+                { type INTEGER }
+                { value 5 }
+            }
+        }
+    }
+    { out
+        {
+            T{ out-binder
+                { toc TOC{ "user" "0" "id" } }
+                { type INTEGER }
+            }
+            T{ out-binder
+                { toc TOC{ "user" "0" "name" } }
+                { type VARCHAR }
+            }
+        }
+    }
+    { errors V{ } }
+}
+] [
+    T{ sqlite-db-connection } db-connection
+    [
+        T{ select
+            { in
+                {
+                    T{ and-binder
+                        { binders
+                            {
+                                T{ greater-than-binder
+                                    { toc TOC{ "qdog" "0" "id" } }
+                                    { type INTEGER }
+                                    { value 0 }
+                                }
+                                T{ less-than-equal-binder
+                                    { toc TOC{ "qdog" "0" "id" } }
+                                    { type INTEGER }
+                                    { value 5 }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            { out
+                {
+                    T{ out-binder
+                        { toc TOC{ "user" "0" "id" } }
+                        { type INTEGER }
+                    }
+                    T{ out-binder
+                        { toc TOC{ "user" "0" "name" } }
+                        { type VARCHAR }
+                    }
+                }
+            }
+            { from { TO{ "user" "0" } } }
+        } query-object>statement
+    ] with-variable
+] unit-test
diff --git a/basis/db2/query-objects/query-objects.factor b/basis/db2/query-objects/query-objects.factor
new file mode 100644 (file)
index 0000000..db0e63a
--- /dev/null
@@ -0,0 +1,212 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors combinators constructors db2.binders
+db2.statements db2.utils kernel make math namespaces sequences
+sequences.deep sets strings ;
+IN: db2.query-objects
+
+TUPLE: query reconstructor ;
+
+TUPLE: insert < query { in sequence } ;
+CONSTRUCTOR: <insert> insert ( -- insert ) ;
+
+TUPLE: update < query { in sequence } { where sequence } ;
+CONSTRUCTOR: <update> update ( -- update ) ;
+
+TUPLE: delete < query { where sequence } ;
+CONSTRUCTOR: <delete> delete ( -- delete ) ;
+
+TUPLE: select < query
+    { in sequence }
+    { out sequence }
+    { from sequence }
+    { join sequence }
+    { offset integer }
+    { limit integer } ;
+CONSTRUCTOR: <select> select ( -- select ) ;
+
+GENERIC: >table-as ( obj -- string )
+GENERIC: >table-name ( in -- string )
+GENERIC: >column-name ( in -- string )
+GENERIC: >qualified-column-name ( in -- string )
+
+M: binder >table-as ( obj -- string )
+    toc>> >table-as ;
+
+M: string >table-as ( string -- string ) ;
+
+M: table-ordinal >table-as ( obj -- string )
+    { table-name>> table-name>> table-ordinal>> } slots
+    append " AS " glue ;
+
+M: in-binder >table-name toc>> table-name>> ;
+M: out-binder >table-name toc>> table-name>> ;
+
+M: in-binder >column-name toc>> column-name>> ;
+M: out-binder >column-name toc>> column-name>> ;
+
+M: count-function >column-name toc>> column-name>> "COUNT(" ")" surround ;
+M: sum-function >column-name toc>> column-name>> "SUM(" ")" surround ;
+M: average-function >column-name toc>> column-name>> "AVG(" ")" surround ;
+M: min-function >column-name toc>> column-name>> "MIN(" ")" surround ;
+M: max-function >column-name toc>> column-name>> "MAX(" ")" surround ;
+M: first-function >column-name toc>> column-name>> "FIRST(" ")" surround ;
+M: last-function >column-name toc>> column-name>> "LAST(" ")" surround ;
+
+: toc>full-name ( toc -- string )
+    { table-name>> table-ordinal>> column-name>> } slots
+    [ append ] dip "." glue ;
+
+M: table-ordinal-column >qualified-column-name toc>full-name ;
+M: in-binder >qualified-column-name toc>> toc>full-name ;
+M: out-binder >qualified-column-name toc>> toc>full-name ;
+M: and-binder >qualified-column-name
+    binders>> [ toc>> toc>full-name ] map ", " join "(" ")" surround ;
+
+M: count-function >qualified-column-name
+    toc>> toc>full-name "COUNT(" ")" surround ;
+M: sum-function >qualified-column-name
+    toc>> toc>full-name "SUM(" ")" surround ;
+M: average-function >qualified-column-name
+    toc>> toc>full-name "AVG(" ")" surround ;
+M: min-function >qualified-column-name
+    toc>> toc>full-name "MIN(" ")" surround ;
+M: max-function >qualified-column-name
+    toc>> toc>full-name "MAX(" ")" surround ;
+M: first-function >qualified-column-name
+    toc>> toc>full-name "FIRST(" ")" surround ;
+M: last-function >qualified-column-name
+    toc>> toc>full-name "LAST(" ")" surround ;
+
+GENERIC: binder-operator ( obj -- string )
+M: equal-binder binder-operator drop " = " ;
+M: not-equal-binder binder-operator drop " <> " ;
+M: less-than-binder binder-operator drop " < " ;
+M: less-than-equal-binder binder-operator drop " <= " ;
+M: greater-than-binder binder-operator drop " > " ;
+M: greater-than-equal-binder binder-operator drop " >= " ;
+
+GENERIC: >bind-pair ( obj -- string )
+: object-bind-pair ( obj -- string )
+    [ >column-name next-bind-index ] [ binder-operator ] bi glue ;
+: special-bind-pair ( obj join-string -- string )
+    [ binders>> [ object-bind-pair ] map ] dip join "(" ")" surround ;
+M: object >bind-pair object-bind-pair ;
+M: and-binder >bind-pair " AND " special-bind-pair ;
+M: or-binder >bind-pair " OR " special-bind-pair ;
+
+: >column/bind-pairs ( seq -- string )
+    [ >bind-pair ] map ", " join ;
+
+GENERIC: >qualified-bind-pair ( obj -- string )
+: qualified-object-bind-pair ( obj -- string )
+    [ >qualified-column-name next-bind-index ] [ binder-operator ] bi glue ;
+: qualified-special-bind-pair ( obj join-string -- string )
+    [ binders>> [ qualified-object-bind-pair ] map ] dip join "(" ")" surround ;
+M: object >qualified-bind-pair qualified-object-bind-pair ;
+M: and-binder >qualified-bind-pair " AND " qualified-special-bind-pair ;
+M: or-binder >qualified-bind-pair " OR " qualified-special-bind-pair ;
+
+: >qualified-column/bind-pairs ( seq -- string )
+    [ >qualified-bind-pair ] map " AND " join ;
+
+: >table-names ( in -- string )
+    [ >table-name ] map members ", " join ;
+
+: >column-names ( in -- string )
+    [ >column-name ] map ", " join ;
+
+: >qualified-column-names ( in -- string )
+    [ >qualified-column-name ] map ", " join ;
+
+: >bind-indices ( in -- string )
+    length [ next-bind-index ] replicate ", " join ;
+
+GENERIC: query-object>statement* ( statement query-object -- statement )
+
+GENERIC: flatten-binder ( obj -- obj' )
+M: in-binder flatten-binder ;
+M: and-binder flatten-binder binders>> [ flatten-binder ] map ;
+M: or-binder flatten-binder binders>> [ flatten-binder ] map ;
+
+: flatten-in ( seq -- seq' )
+    [
+        [ flatten-binder , ] each
+    ] { } make flatten ;
+
+M: insert query-object>statement*
+    [ "INSERT INTO " add-sql ] dip {
+        [ in>> first >table-name add-sql " (" add-sql ]
+        [ in>> >column-names add-sql ") VALUES(" add-sql ]
+        [ in>> >bind-indices add-sql ");" add-sql ]
+        [ in>> flatten-in >>in ]
+    } cleave ;
+
+: seq>where ( statement seq -- statement )
+    [
+        [ " WHERE " add-sql ] dip
+        >column/bind-pairs add-sql
+    ] unless-empty ;
+
+: qualified-seq>where ( statement seq -- statement )
+    [
+        [ " WHERE " add-sql ] dip
+        >qualified-column/bind-pairs add-sql
+    ] unless-empty ;
+
+: renamed-table-names ( seq -- string )
+    [ >table-as ] map ", " join ;
+
+: select-from ( select -- string )
+    from>> ?1array renamed-table-names ;
+
+GENERIC: >join-string ( join-binder -- string )
+
+M: join-binder >join-string
+    [ toc2>> >table-as " LEFT JOIN " " ON " surround ]
+    [ toc1>> >qualified-column-name ]
+    [ toc2>> >qualified-column-name ]
+    ! [ toc2>> { table-name>> column-name>> } slots "." glue ]
+    tri " = " glue append ;
+
+: select-join ( select -- string )
+    join>> [
+        ""
+    ] [
+        [ >join-string ] map ", " join
+    ] if-empty ;
+
+M: select query-object>statement*
+    [ "SELECT " add-sql ] dip {
+        [ out>> >qualified-column-names add-sql " FROM " add-sql ]
+        [ select-from add-sql ]
+        [ select-join add-sql ]
+        [ in>> qualified-seq>where ";" add-sql ]
+        [ out>> >>out ]
+        [ in>> flatten-in >>in ]
+    } cleave ;
+
+M: update query-object>statement*
+    [ "UPDATE " add-sql ] dip {
+        [ in>> >table-names add-sql " SET " add-sql ]
+        [ in>> >column/bind-pairs add-sql ]
+        [ where>> seq>where ";" add-sql ]
+        [ { in>> where>> } slots append flatten-in >>in ]
+    } cleave ;
+
+M: delete query-object>statement*
+    [ "DELETE FROM " add-sql ] dip {
+        [ where>> >table-names add-sql ]
+        [ where>> seq>where ";" add-sql ]
+        [ where>> flatten-in >>in ]
+    } cleave ;
+
+: query-object>statement ( object1 -- object2 )
+    [
+        init-bind-index
+        [ <statement> ] dip {
+            [ query-object>statement* ]
+            [ reconstructor>> >>reconstructor ]
+        } cleave
+        ! normalize-fql
+    ] with-scope ;
diff --git a/basis/db2/result-sets/authors.txt b/basis/db2/result-sets/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/db2/result-sets/result-sets.factor b/basis/db2/result-sets/result-sets.factor
new file mode 100644 (file)
index 0000000..d13836c
--- /dev/null
@@ -0,0 +1,65 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors combinators db2.binders db2.connections
+db2.types
+destructors fry kernel namespaces sequences ;
+IN: db2.result-sets
+
+TUPLE: result-set handle sql in out n max ;
+
+GENERIC: #rows ( result-set -- n )
+GENERIC: #columns ( result-set -- n )
+GENERIC: advance-row ( result-set -- )
+GENERIC: more-rows? ( result-set -- ? )
+GENERIC#: column 2 ( result-set column type -- sql )
+GENERIC: get-type ( binder/word -- type )
+HOOK: statement>result-set db-connection ( statement -- result-set )
+
+: init-result-set ( result-set -- result-set )
+    dup #rows >>max
+    0 >>n ; inline
+
+: new-result-set ( query handle class -- result-set )
+    new
+        swap >>handle
+        swap {
+            [ sql>> >>sql ]
+            [ in>> >>in ]
+            [ out>> >>out ]
+        } cleave ; inline
+
+ERROR: result-set-length-mismatch result-set #columns out-length ;
+
+: validate-result-set ( result-set -- result-set )
+    dup [ #columns ] [ out>> length ] bi 2dup = [
+        2drop
+    ] [
+        result-set-length-mismatch
+    ] if ;
+
+: sql-row ( result-set -- seq )
+    [ #columns <iota> ] [ out>> ] [ ] tri over empty? [
+        nip
+        '[ [ _ ] dip VARCHAR column ] map
+    ] [
+        validate-result-set
+        '[ [ _ ] 2dip get-type column ] 2map
+    ] if ;
+
+M: sql-type get-type ;
+
+M: out-binder get-type type>> ;
+
+M: out-binder-low get-type type>> ;
+
+: result-set-each ( statement quot: ( statement -- ) -- )
+    over more-rows?
+    [ [ call ] 2keep over advance-row result-set-each ]
+    [ 2drop ] if ; inline recursive
+
+: result-set-map ( statement quot -- sequence )
+    collector [ result-set-each ] dip { } like ; inline
+
+: statement>result-sequence ( statement -- sequence )
+    statement>result-set
+    [ [ sql-row ] result-set-map ] with-disposal ;
diff --git a/basis/db2/statements/authors.txt b/basis/db2/statements/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/db2/statements/statements.factor b/basis/db2/statements/statements.factor
new file mode 100644 (file)
index 0000000..d2c242b
--- /dev/null
@@ -0,0 +1,77 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors arrays continuations db2.connections db2.errors
+db2.result-sets db2.utils destructors fry kernel sequences math
+vectors ;
+IN: db2.statements
+
+TUPLE: statement handle sql in out after
+retries errors retry-quotation reconstructor ;
+
+: normalize-statement ( statement -- statement )
+    [ object>vector ] change-in
+    [ object>vector ] change-out ; inline
+
+: initialize-statement ( statement -- statement )
+    V{ } clone >>in
+    V{ } clone >>out
+    V{ } clone >>errors ; inline
+: <sql> ( string -- statement )
+    statement new
+        swap >>sql
+        initialize-statement ; inline
+
+: <statement> ( -- statement )
+    statement new
+        initialize-statement ; inline
+
+HOOK: next-bind-index db-connection ( -- string )
+HOOK: init-bind-index db-connection ( -- )
+
+: add-sql ( statement sql -- statement )
+    '[ _ "" append-as ] change-sql ;
+
+GENERIC: add-in ( statement object -- statement )
+GENERIC: add-out ( statement object -- statement )
+
+: in-vector ( statmenet object -- statement object statement )
+    over [ >vector ] change-in in>> ;
+
+: out-vector ( statmenet object -- statement object statement )
+    over [ >vector ] change-out out>> ;
+
+M: sequence add-in in-vector push-all ;
+M: object add-in in-vector push ;
+M: sequence add-out out-vector push-all ;
+M: object add-out out-vector push ;
+
+HOOK: prepare-statement* db-connection ( statement -- statement' )
+HOOK: dispose-statement db-connection ( statement -- )
+HOOK: bind-sequence db-connection ( statement -- )
+HOOK: reset-statement db-connection ( statement -- statement' )
+
+ERROR: no-database-in-scope ;
+
+M: statement dispose dispose-statement ;
+M: f dispose-statement no-database-in-scope ;
+M: object reset-statement ;
+
+: with-sql-error-handler ( quot -- )
+    [ dup sql-error? [ parse-sql-error ] when rethrow ] recover ; inline
+
+: prepare-statement ( statement -- statement )
+    [ dup handle>> [ prepare-statement* ] unless ] with-sql-error-handler ;
+
+: (run-after-setters) ( tuple statement -- )
+    after>> [
+        [ value>> ] [ setter>> ] bi
+        call( obj val -- obj ) drop
+    ] with each ;
+
+: run-after-setters ( tuple statement -- )
+    dup sequence? [
+        [ (run-after-setters) ] with each
+    ] [
+        (run-after-setters)
+    ] if ;
diff --git a/basis/db2/statements/tests/tests.factor b/basis/db2/statements/tests/tests.factor
new file mode 100644 (file)
index 0000000..5ac8350
--- /dev/null
@@ -0,0 +1,57 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors continuations db2 db2.debug db2.errors
+db2.result-sets db2.statements db2.types kernel multiline
+tools.test ;
+IN: db2.statements.tests
+
+{ 1 0 } [ [ drop ] result-set-each ] must-infer-as
+{ 1 1 } [ [ ] result-set-map ] must-infer-as
+
+: create-computer-table ( -- )
+    [ "drop table computer;" sql-command ] ignore-errors
+
+    ! [ "drop table computer;" sql-command ]
+    ! [ [ sql-table-missing? ] [ table>> "computer" = ] bi and ] must-fail-with
+
+    [ "drop table computer;" sql-command ] must-fail
+
+    [ ] [
+        "create table computer(name varchar, os varchar, version integer);"
+        sql-command
+    ] unit-test ;
+
+: test-sql-command ( -- )
+    create-computer-table
+
+    [ ] [
+        "insert into computer (name, os) values('rocky', 'mac');"
+        sql-command
+    ] unit-test
+
+    [ ] [
+        <statement>
+            "insert into computer (name, os) values('vio', 'opp');" >>sql
+        sql-command
+    ] unit-test
+
+    [ { { "rocky" "mac" } { "vio" "opp" } } ]
+    [
+        <statement>
+            "select name, os from computer;" >>sql
+        sql-query
+    ] unit-test
+
+    ! [ "insert into" sql-command ] [ sql-syntax-error? ] must-fail-with
+
+    ! [ "selectt" sql-query drop ] [ sql-syntax-error? ] must-fail-with
+
+    [ "drop table default_person" sql-command ] ignore-errors
+
+    [ ] [
+        <statement>
+            "create table default_person(id serial primary key, name text, birthdate timestamp, email text, homepage text)" >>sql
+        sql-command
+    ] unit-test ;
+
+[ test-sql-command ] test-dbs
diff --git a/basis/db2/tester/tester.factor b/basis/db2/tester/tester.factor
new file mode 100644 (file)
index 0000000..9daa83e
--- /dev/null
@@ -0,0 +1,64 @@
+! Copyright (C) 2008 Slava Pestov, Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors concurrency.combinators db2 db2.pools db2.types
+fry io io.files.temp kernel math math.parser multiline
+namespaces postgresql.db2 prettyprint random sequences
+sqlite.db2 system threads tools.test ;
+IN: db2.tester
+
+/*
+TUPLE: test-1 id a b c ;
+
+test-1 "TEST1" {
+   { "id" "ID" INTEGER +db-assigned-id+ }
+   { "a" "A" { VARCHAR 256 } +not-null+ }
+   { "b" "B" { VARCHAR 256 } +not-null+ }
+   { "c" "C" { VARCHAR 256 } +not-null+ }
+} make-persistent
+
+TUPLE: test-2 id x y z ;
+
+test-2 "TEST2" {
+   { "id" "ID" INTEGER +db-assigned-id+ }
+   { "x" "X" { VARCHAR 256 } +not-null+ }
+   { "y" "Y" { VARCHAR 256 } +not-null+ }
+   { "z" "Z" { VARCHAR 256 } +not-null+ }
+} make-persistent
+
+: test-1-tuple ( -- tuple )
+    f 100 random 100 random 100 random [ number>string ] tri@
+    test-1 boa ;
+
+: db-tester ( test-db -- )
+    [
+        [
+            test-1 ensure-table
+            test-2 ensure-table
+        ] with-db
+    ] [
+        10 iota [
+            drop
+            10 [
+                dup [
+                    test-1-tuple insert-tuple yield
+                ] with-db
+            ] times
+        ] with parallel-each
+    ] bi ;
+
+: db-tester2 ( test-db -- )
+    [
+        [
+            test-1 ensure-table
+            test-2 ensure-table
+        ] with-db
+    ] [
+        <db-pool> [
+            10 iota [
+                10 [
+                    test-1-tuple insert-tuple yield
+                ] times
+            ] parallel-each
+        ] with-pooled-db
+    ] bi ;
+*/
diff --git a/basis/db2/transactions/authors.txt b/basis/db2/transactions/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/db2/transactions/transactions.factor b/basis/db2/transactions/transactions.factor
new file mode 100644 (file)
index 0000000..fd0e6ad
--- /dev/null
@@ -0,0 +1,26 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: continuations db2 db2.connections namespaces ;
+IN: db2.transactions
+
+SYMBOL: in-transaction
+
+HOOK: begin-transaction db-connection ( -- )
+
+HOOK: commit-transaction db-connection ( -- )
+
+HOOK: rollback-transaction db-connection ( -- )
+
+M: db-connection begin-transaction ( -- ) "BEGIN" sql-command ;
+
+M: db-connection commit-transaction ( -- ) "COMMIT" sql-command ;
+
+M: db-connection rollback-transaction ( -- ) "ROLLBACK" sql-command ;
+
+: in-transaction? ( -- ? ) in-transaction get ;
+
+: with-transaction ( quot -- )
+    t in-transaction [
+        begin-transaction
+        [ ] [ rollback-transaction ] cleanup commit-transaction
+    ] with-variable ; inline
diff --git a/basis/db2/types/authors.txt b/basis/db2/types/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/db2/types/types.factor b/basis/db2/types/types.factor
new file mode 100644 (file)
index 0000000..2d04341
--- /dev/null
@@ -0,0 +1,98 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: arrays classes.mixin classes.parser classes.singleton
+combinators db2.connections kernel lexer sequences ;
+IN: db2.types
+
+HOOK: sql-type>string db-connection ( type -- string )
+HOOK: sql-create-type>string db-connection ( type -- string )
+HOOK: sql-modifiers>string db-connection ( modifiers -- string )
+HOOK: db-type>fql-type db-connection ( name -- table-schema )
+
+HOOK: persistent-type-hashtable db-connection ( -- hashtable )
+
+MIXIN: sql-type
+MIXIN: sql-modifier
+MIXIN: sql-primary-key
+
+INSTANCE: sql-primary-key sql-modifier
+
+<<
+
+: define-sql-instance ( word mixin -- )
+    over define-singleton-class
+    add-mixin-instance ;
+
+: define-sql-type ( word -- )
+    sql-type define-sql-instance ;
+
+: define-sql-modifier ( word -- )
+    sql-modifier define-sql-instance ;
+
+: define-primary-key ( word -- )
+    [ define-sql-type ]
+    [ sql-primary-key add-mixin-instance ] bi ;
+
+SYNTAX: SQL-TYPE:
+    scan-new-class define-sql-type ;
+
+SYNTAX: SQL-TYPES:
+    ";" parse-tokens
+    [ create-class-in define-sql-type ] each ;
+
+SYNTAX: PRIMARY-KEY-TYPE:
+    scan-new-class define-primary-key ;
+
+SYNTAX: PRIMARY-KEY-TYPES:
+    ";" parse-tokens
+    [ create-class-in define-primary-key ] each ;
+
+SYNTAX: SQL-MODIFIER:
+    scan-new-class define-sql-modifier ;
+
+SYNTAX: SQL-MODIFIERS:
+    ";" parse-tokens
+    [ create-class-in define-sql-modifier ] each ;
+
+>>
+
+SQL-TYPES:
+    INTEGER BIG-INTEGER SIGNED-BIG-INTEGER UNSIGNED-BIG-INTEGER
+    DOUBLE REAL
+    BOOLEAN
+    TEXT CHARACTER VARCHAR DATE
+    TIME DATETIME TIMESTAMP
+    BLOB FACTOR-BLOB
+    URL ;
+
+! Delete +not-null+
+SQL-MODIFIERS: SERIAL AUTOINCREMENT UNIQUE DEFAULT NOT-NULL NULL
++on-update+ +on-delete+ +restrict+ +cascade+ +set-null+ +set-default+
++not-null+ +system-random-generator+ ;
+
+PRIMARY-KEY-TYPES: +db-assigned-key+
+    +user-assigned-key+
+    +random-key+
+    +primary-key+ ;
+
+INSTANCE: +user-assigned-key+ sql-modifier
+INSTANCE: +db-assigned-key+ sql-modifier
+
+SYMBOL: IGNORE
+
+ERROR: no-sql-type name ;
+ERROR: no-sql-modifier name ;
+
+: ensure-sql-type ( object -- object )
+    dup sql-type? [ no-sql-type ] unless ;
+
+: ensure-sql-modifier ( object -- object )
+    dup sql-modifier? [ no-sql-modifier ] unless ;
+
+: persistent-type>sql-type ( type -- type' )
+    dup array? [ first ] when
+    {
+        { +db-assigned-key+ [ INTEGER ] }
+        { +random-key+ [ INTEGER ] }
+        [ ]
+    } case ;
diff --git a/basis/db2/utils/authors.txt b/basis/db2/utils/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/db2/utils/utils.factor b/basis/db2/utils/utils.factor
new file mode 100644 (file)
index 0000000..0b40f66
--- /dev/null
@@ -0,0 +1,121 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors alien.c-types alien.data arrays assocs classes
+classes.algebra classes.tuple combinators
+combinators.short-circuit fry kernel libc locals macros math
+math.order math.parser quotations sequences sequences.private
+slots slots.private strings vectors words ;
+IN: db2.utils
+
+SLOT: slot-name
+
+MACRO: slots ( seq -- quot )
+    [ 1quotation ] map '[ _ cleave ] ;
+
+: subclass? ( class1 class2 -- ? )
+    { [ class<= ] [ drop tuple-class? ] } 2&& ;
+
+: quote-sql-name ( string -- string' ) "\"" dup surround ; 
+
+: sql-name-replace ( string -- string' )
+    H{ { CHAR: - CHAR: _ } { CHAR: ? CHAR: p } } substitute ;
+
+: malloc-byte-array/length ( byte-array -- alien length )
+    [ malloc-byte-array &free ] [ length ] bi ;
+
+: object>vector ( obj -- vector )
+    dup sequence? [ >vector ] [ 1vector ] if ;
+
+: trim-double-quotes ( string -- string' )
+    [ CHAR: " = ] trim ;
+
+: ?when ( object quot -- object' ) dupd when ; inline
+
+: ?1array ( obj -- array )
+    dup { [ array? ] [ vector? ] } 1|| [ 1array ] unless ; inline
+
+: ??1array ( obj -- array/f ) [ ?1array ] ?when ; inline
+
+: ?second ( sequence -- object/f ) 1 swap ?nth ;
+: ?third ( sequence -- object/f ) 2 swap ?nth ;
+
+: ?first2 ( sequence -- object1/f object2/f )
+    [ ?first ] [ ?second ] bi ;
+
+: ?first3 ( sequence -- object1/f object2/f object3/f )
+    [ ?first ] [ ?second ] [ ?third ] tri ;
+
+:: 2interleave ( seq1 seq2 between: ( -- ) quot: ( obj1 obj2 -- ) -- )
+    { [ seq1 empty? ] [ seq2 empty? ] } 0|| [
+        seq1 seq2 [ first-unsafe ] bi@ quot call
+        seq1 seq2 [ rest-slice ] bi@
+        2dup { [ nip empty? ] [ drop empty? ] } 2|| [
+            2drop
+        ] [
+            between call
+            between quot 2interleave
+        ] if
+    ] unless ; inline recursive
+
+: assoc-with ( object sequence quot -- obj curry )
+    swapd [ [ -rot ] dip  call ] 2curry ; inline
+
+: ?number>string ( n/string -- string )
+    dup number? [ number>string ] when ;
+
+ERROR: no-accessor name ;
+
+: lookup-accessor ( string -- accessor )
+    dup "accessors" lookup-word [ nip ] [ no-accessor ] if* ;
+
+: lookup-getter ( string -- accessor )
+    ">>" append lookup-accessor ;
+
+: lookup-setter ( string -- accessor )
+    ">>" prepend lookup-accessor ;
+
+ERROR: string-expected object ;
+
+: ensure-string ( object -- string )
+    dup string? [ string-expected ] unless ;
+
+ERROR: length-expected-range seq from to ;
+: ensure-length-range ( seq from to -- seq )
+    3dup [ length ] 2dip between? [
+        2drop
+    ] [
+        length-expected-range
+    ] if ;
+
+ERROR: length-expected seq length ;
+: ensure-length ( seq length -- seq )
+    2dup [ length ] dip = [
+        drop
+    ] [
+        length-expected
+    ] if ;
+
+: new-filled-tuple ( class values setters -- tuple )
+    [ new ] 2dip [ call( tuple obj -- tuple ) ] 2each ;
+
+ERROR: no-slot name specs ;
+
+: offset-of-slot ( string tuple -- n )
+    class-of superclasses-of [ "slots" word-prop ] map concat
+    2dup slot-named [ 2nip offset>> ] [ no-slot ] if* ;
+
+: get-slot-named ( name tuple -- value )
+    [ nip ] [ offset-of-slot ] 2bi slot ;
+
+: set-slot-named ( value name tuple -- )
+    [ nip ] [ offset-of-slot ] 2bi set-slot ;
+
+: change-slot-named ( name tuple quot -- tuple )
+    [ [ get-slot-named ] dip call( obj -- obj' ) ]
+    [ drop [ set-slot-named ] keep ] 3bi ;
+
+: filter-slots ( tuple specs -- specs' )
+    [
+        slot-name>> swap get-slot-named
+        ! dup double-infinite-interval? [ drop f ] when
+    ] with filter ;
diff --git a/basis/mysql/authors.txt b/basis/mysql/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/mysql/db2/authors.txt b/basis/mysql/db2/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/mysql/db2/connections/authors.txt b/basis/mysql/db2/connections/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/mysql/db2/connections/connections.factor b/basis/mysql/db2/connections/connections.factor
new file mode 100644 (file)
index 0000000..40110c3
--- /dev/null
@@ -0,0 +1,22 @@
+! Copyright (C) 2011 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors combinators db2.connections destructors kernel
+mysql.db2 mysql.db2.ffi mysql.db2.lib ;
+IN: mysql.db2.connections
+
+TUPLE: mysql-db-connection < db-connection ;
+
+: <mysql-db-connection> ( handle -- db-connection )
+    mysql-db-connection new-db-connection ; inline
+
+M: mysql-db db>db-connection-generic ( db -- db-connection )
+    {
+        [ host>> ]
+        [ username>> ]
+        [ password>> ]
+        [ database>> ]
+        [ port>> ]
+    } cleave mysql-connect <mysql-db-connection> ;
+
+M: mysql-db-connection dispose*
+    [ handle>> mysql_close ] [ f >>handle drop ] bi ;
diff --git a/basis/mysql/db2/db2.factor b/basis/mysql/db2/db2.factor
new file mode 100644 (file)
index 0000000..7fed7fa
--- /dev/null
@@ -0,0 +1,17 @@
+! Copyright (C) 2011 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: kernel sequences vocabs vocabs.loader ;
+IN: mysql.db2
+
+TUPLE: mysql-db host username password database port ;
+
+: <mysql-db> ( -- db )
+    f f f f 0 mysql-db boa ;
+
+{
+    "mysql.db2.ffi"
+    "mysql.db2.lib"
+    "mysql.db2.connections"
+    "mysql.db2.statements"
+    "mysql.db2.result-sets"
+} [ require ] each
diff --git a/basis/mysql/db2/ffi/authors.txt b/basis/mysql/db2/ffi/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/mysql/db2/ffi/ffi.factor b/basis/mysql/db2/ffi/ffi.factor
new file mode 100644 (file)
index 0000000..5340071
--- /dev/null
@@ -0,0 +1,676 @@
+! Copyright (C) 2010 John Benediktsson
+! See http://factorcode.org/license.txt for BSD license.
+USING: alien alien.c-types alien.syntax classes.struct
+combinators system alien.libraries ;
+IN: mysql.db2.ffi
+
+! Mysql 5.7.11, 3/6/2016
+<< "mysql" {
+    { [ os windows? ]  [ "libmysql.dll" ] }
+    { [ os macosx? ] [ "libmysqlclient.dylib" ] }
+    { [ os unix?  ]  [ "libmysqlclient.so" ] }
+} cond cdecl add-library >>
+
+LIBRARY: mysql
+
+TYPEDEF: int my_socket
+TYPEDEF: char my_bool
+
+CONSTANT: MYSQL_ERRMSG_SIZE 512
+CONSTANT: SCRAMBLE_LENGTH 20
+
+ENUM: mysql_status
+    MYSQL_STATUS_READY
+    MYSQL_STATUS_GET_RESULT
+    MYSQL_STATUS_USE_RESULT ;
+
+CONSTANT: MYSQL_NO_DATA 100
+CONSTANT: MYSQL_DATA_TRUNCATED 101
+
+ENUM: mysql_option
+    MYSQL_OPT_CONNECT_TIMEOUT
+    MYSQL_OPT_COMPRESS
+    MYSQL_OPT_NAMED_PIPE
+    MYSQL_INIT_COMMAND
+    MYSQL_READ_DEFAULT_FILE
+    MYSQL_READ_DEFAULT_GROUP
+    MYSQL_SET_CHARSET_DIR
+    MYSQL_SET_CHARSET_NAME
+    MYSQL_OPT_LOCAL_INFILE
+    MYSQL_OPT_PROTOCOL
+    MYSQL_SHARED_MEMORY_BASE_NAME
+    MYSQL_OPT_READ_TIMEOUT
+    MYSQL_OPT_WRITE_TIMEOUT
+    MYSQL_OPT_USE_RESULT
+    MYSQL_OPT_USE_REMOTE_CONNECTION
+    MYSQL_OPT_USE_EMBEDDED_CONNECTION
+    MYSQL_OPT_GUESS_CONNECTION
+    MYSQL_SET_CLIENT_IP
+    MYSQL_SECURE_AUTH
+    MYSQL_REPORT_DATA_TRUNCATION
+    MYSQL_OPT_RECONNECT
+    MYSQL_OPT_SSL_VERIFY_SERVER_CERT ;
+
+ENUM: mysql_protocol_type
+    MYSQL_PROTOCOL_DEFAULT
+    MYSQL_PROTOCOL_TCP
+    MYSQL_PROTOCOL_SOCKET
+    MYSQL_PROTOCOL_PIPE
+    MYSQL_PROTOCOL_MEMORY ;
+
+ENUM: mysql_rpl_type
+    MYSQL_RPL_MASTER
+    MYSQL_RPL_SLAVE
+    MYSQL_RPL_ADMIN ;
+
+ENUM: enum_field_types
+    MYSQL_TYPE_DECIMAL
+    MYSQL_TYPE_TINY
+    MYSQL_TYPE_SHORT
+    MYSQL_TYPE_LONG
+    MYSQL_TYPE_FLOAT
+    MYSQL_TYPE_DOUBLE
+    MYSQL_TYPE_NULL
+    MYSQL_TYPE_TIMESTAMP
+    MYSQL_TYPE_LONGLONG
+    MYSQL_TYPE_INT24
+    MYSQL_TYPE_DATE
+    MYSQL_TYPE_TIME
+    MYSQL_TYPE_DATETIME
+    MYSQL_TYPE_YEAR
+    MYSQL_TYPE_NEWDATE
+    MYSQL_TYPE_VARCHAR
+    MYSQL_TYPE_BIT
+    { MYSQL_TYPE_NEWDECIMAL 246 }
+    { MYSQL_TYPE_ENUM 247 }
+    { MYSQL_TYPE_SET 248 }
+    { MYSQL_TYPE_TINY_BLOB 249 }
+    { MYSQL_TYPE_MEDIUM_BLOB 250 }
+    { MYSQL_TYPE_LONG_BLOB 251 }
+    { MYSQL_TYPE_BLOB 252 }
+    { MYSQL_TYPE_VAR_STRING 253 }
+    { MYSQL_TYPE_STRING 254 }
+    { MYSQL_TYPE_GEOMETRY 255 } ;
+
+ENUM: enum_mysql_stmt_state
+    { MYSQL_STMT_INIT_DONE 1 }
+    MYSQL_STMT_PREPARE_DONE
+    MYSQL_STMT_EXECUTE_DONE
+    MYSQL_STMT_FETCH_DONE ;
+
+ENUM: enum_stmt_attr_type
+    STMT_ATTR_UPDATE_MAX_LENGTH
+    STMT_ATTR_CURSOR_TYPE
+    STMT_ATTR_PREFETCH_ROWS ;
+
+! st_list
+STRUCT: LIST
+    { prev LIST* }
+    { next LIST* }
+    { data void* } ;
+
+
+STRUCT: USED_MEM
+    { next USED_MEM* }
+    { left uint }
+    { size uint } ;
+
+TYPEDEF: uint PSI_memory_key
+
+STRUCT: MEM_ROOT
+    { free USED_MEM* }
+    { used USED_MEM* }
+    { pre_alloc USED_MEM* }
+    { min_malloc size_t }
+    { block_size size_t }
+    { block_num uint }
+    { first_block_usage uint }
+    { max_capacity size_t }
+    { allocated_size size_t }
+    { error_for_capacity_exceeded my_bool }
+    { error_handler void* }
+    { m_psi_key PSI_memory_key } ;
+
+! st_mysql_field
+STRUCT: MYSQL_FIELD
+    { name c-string }
+    { org_name c-string }
+    { table c-string }
+    { org_table c-string }
+    { db c-string }
+    { catalog c-string }
+    { def c-string }
+    { length ulong }
+    { max_length ulong }
+    { name_length uint }
+    { org_name_length uint }
+    { table_length uint }
+    { org_table_length uint }
+    { db_length uint }
+    { catalog_length uint }
+    { def_length uint }
+    { flags uint }
+    { decimals uint }
+    { charsetnr uint }
+    { type enum_field_types }
+    { extension void* } ;
+
+STRUCT: st_dynamic_array
+    { buffer uchar* }
+    { elements uint }
+    { max_element uint }
+    { alloc_increment uint }
+    { size_of_element uint } ;
+
+STRUCT: st_mysql_options
+    { connect_timeout uint }
+    { read_timeout uint }
+    { write_timeout uint }
+    { port uint }
+    { protocol uint }
+    { client_flag ulong }
+    { host c-string }
+    { user c-string }
+    { password c-string }
+    { unix_socket c-string }
+    { db c-string }
+    { init_commands st_dynamic_array* }
+    { my_cnf_file c-string }
+    { my_cnf_group c-string }
+    { charset_dir c-string }
+    { charset_name c-string }
+    { ssl_key c-string }
+    { ssl_cert c-string }
+    { ssl_ca c-string }
+    { ssl_capath c-string }
+    { ssl_cipher c-string }
+    { shared_memory_base_name c-string }
+    { max_allowed_packet ulong }
+    { use_ssl my_bool }
+    { compress my_bool }
+    { named_pipe my_bool }
+    { rpl_probe my_bool }
+    { rpl_parse my_bool }
+    { no_master_reads my_bool }
+    { methods_to_use mysql_option }
+    { client_ip c-string }
+    { secure_auth my_bool }
+    { report_data_truncation my_bool }
+    { local_infile_init void* }
+    { local_infile_read void* }
+    { local_infile_end void* }
+    { local_infile_error void* }
+    { local_infile_userdata void* }
+    { extension void* } ;
+
+! my_uni_idx_st
+STRUCT: MY_UNI_IDX
+    { from ushort }
+    { to ushort }
+    { tab uchar* } ;
+
+! unicase_info_st
+STRUCT: MY_UNICASE_INFO
+    { toupper ushort }
+    { tolower ushort }
+    { sort ushort } ;
+
+STRUCT: charset_info_st
+    { number uint }
+    { primary_number uint }
+    { binary_number uint }
+    { state uint }
+    { csname c-string }
+    { name c-string }
+    { comment c-string }
+    { tailoring c-string }
+    { ctype c-string }
+    { to_lower c-string }
+    { to_upper c-string }
+    { sort_order c-string }
+    { contractions ushort* }
+    { sort_order_big ushort** }
+    { tab_to_uni ushort* }
+    { tab_from_uni MY_UNI_IDX* }
+    { caseinfo MY_UNICASE_INFO** }
+    { state_map c-string }
+    { ident_map c-string }
+    { strxfrm_multiply uint }
+    { caseup_multiply uchar }
+    { casedn_multiply uchar }
+    { mbminlen uint }
+    { mbmaxlen uint }
+    { min_sort_char ushort }
+    { max_sort_char ushort }
+    { pad_char uchar }
+    { escape_with_backslash_is_dangerous char }
+    { cset void* }
+    { coll void* } ;
+
+C-TYPE: Vio
+! st_net
+STRUCT: NET
+    { vio Vio* }
+    { buff uchar* }
+    { buff_end uchar* }
+    { write_pos uchar* }
+    { read_pos uchar* }
+    { fd my_socket }
+    { remain_in_buf ulong }
+    { length ulong }
+    { buf_length ulong }
+    { where_b ulong }
+    { max_packet ulong }
+    { max_packet_size ulong }
+    { pkt_nr uint }
+    { compress_pkt_nr uint }
+    { write_timeout uint }
+    { read_timeout uint }
+    { retry_count uint }
+    { fcntl int }
+    { return_status uint* }
+    { reading_or_writing uchar }
+    { save_char char }
+    { unused1 my_bool }
+    { unused2 my_bool }
+    { compress my_bool }
+    { unused3 my_bool }
+    { query_cache_query uchar* }
+    { last_errno uint }
+    { error uchar }
+    { unused4 my_bool }
+    { unused5 my_bool }
+    { last_error char[512] }
+    { sqlstate char[6] }
+    { extension void* } ;
+
+STRUCT: MYSQL
+    { net NET }
+    { connector_fd uchar* }
+    { host c-string }
+    { user c-string }
+    { passwd c-string }
+    { unix_socket c-string }
+    { server_version c-string }
+    { host_info c-string }
+    { info c-string }
+    { db c-string }
+    { charset charset_info_st* }
+    { fields MYSQL_FIELD* }
+    { field_alloc MEM_ROOT }
+    { affected_rows ulonglong }
+    { insert_id ulonglong }
+    { extra_info ulonglong }
+    { thread_id ulong }
+    { packet_length ulong }
+    { port uint }
+    { client_flag ulong }
+    { server_capabilities ulong }
+    { protocol_version uint }
+    { field_count uint }
+    { server_status uint }
+    { server_language uint }
+    { warning_count uint }
+    { options st_mysql_options }
+    { status mysql_status }
+    { free_me bool }
+    { reconnect bool }
+    { scramble char[21] }
+    { rpl_pivot my_bool }
+    { master MYSQL* }
+    { next_slave MYSQL* }
+    { last_used_slave MYSQL* }
+    { last_used_con MYSQL* }
+    { stmts LIST* }
+    { methods void* }
+    { thd void* }
+    { unbuffered_fetch_owner bool* }
+    { info_buffer c-string }
+    { extension void* } ;
+
+TYPEDEF: c-string* MYSQL_ROW
+
+STRUCT: MYSQL_ROWS
+    { next MYSQL_ROWS* }
+    { data MYSQL_ROW }
+    { length ulong } ;
+
+TYPEDEF: MYSQL_ROWS* MYSQL_ROW_OFFSET
+
+STRUCT: MYSQL_DATA
+    { data MYSQL_ROWS* }
+    { embedded_info void* }
+    { alloc MEM_ROOT }
+    { rows ulonglong }
+    { fields uint }
+    { extension void* } ;
+
+STRUCT: MYSQL_RES
+    { row_count ulonglong }
+    { fields MYSQL_FIELD* }
+    { data MYSQL_DATA* }
+    { data_cursor MYSQL_ROWS* }
+    { lengths ulong* }
+    { handle MYSQL* }
+    { methods void* }
+    { row MYSQL_ROW }
+    { current_row MYSQL_ROW }
+    { field_alloc MEM_ROOT }
+    { field_count uint }
+    { current_field uint }
+    { eof bool }
+    { unbuffered_fetch_cancelled bool }
+    { extension void* } ;
+
+
+STRUCT: MYSQL_BIND
+    { length ulong* }
+    { is_null bool* }
+    { buffer void* }
+    { error bool* }
+    { row_ptr uchar* }
+    { store_param_func void* }
+    { fetch_result void* }
+    { skip_result void* }
+    { buffer_length ulong }
+    { offset ulong }
+    { length_value ulong }
+    { param_number uint }
+    { pack_length uint }
+    { buffer_type enum_field_types }
+    { error_value bool }
+    { is_unsigned bool }
+    { long_data_used bool }
+    { is_null_value bool }
+    { extension void* } ;
+
+
+
+! FIXME: Replace with TYPEDEF: void* MYSQL_STMT
+! since no fields are supposed to be used by application?
+
+STRUCT: MYSQL_STMT
+    { mem_root MEM_ROOT }
+    { list LIST }
+    { mysql MYSQL* }
+    { params MYSQL_BIND* }
+    { bind MYSQL_BIND* }
+    { fields MYSQL_FIELD* }
+    { result MYSQL_DATA }
+    { data_cursor MYSQL_ROWS* }
+    { read_row_func void* }
+    { affected_rows ulonglong }
+    { insert_id ulonglong }
+    { stmt_id ulong }
+    { flags ulong }
+    { prefetch_rows ulong }
+    { server_status uint }
+    { last_errno uint }
+    { param_count uint }
+    { field_count uint }
+    { state enum_mysql_stmt_state }
+    { last_error char[MYSQL_ERRMSG_SIZE] }
+    { sqlstate char[6] }
+    { send_types_to_server bool }
+    { bind_param_done bool }
+    { bind_result_done uchar }
+    { unbuffered_fetch_cancelled bool }
+    { update_max_length bool }
+    { extension void* } ;
+
+
+ENUM: enum_mysql_timestamp_type
+    { MYSQL_TIMESTAMP_NONE -2 }
+    { MYSQL_TIMESTAMP_ERROR -1 }
+    { MYSQL_TIMESTAMP_DATE 0 }
+    { MYSQL_TIMESTAMP_DATETIME 1 }
+    { MYSQL_TIMESTAMP_TIME 2 } ;
+
+
+STRUCT: MYSQL_TIME
+    { year uint }
+    { month uint }
+    { day uint }
+    { hour uint }
+    { minute uint }
+    { second uint }
+    { second_part ulong }
+    { neg bool }
+    { time_type enum_mysql_timestamp_type } ;
+
+
+
+FUNCTION: MYSQL* mysql_init ( MYSQL* mysql )
+
+
+FUNCTION: c-string mysql_info ( MYSQL* mysql )
+
+
+
+FUNCTION: uint mysql_errno ( MYSQL* mysql )
+
+FUNCTION: c-string mysql_error ( MYSQL* mysql )
+
+
+FUNCTION: c-string mysql_get_client_info ( )
+
+FUNCTION: ulong mysql_get_client_version ( )
+
+FUNCTION: c-string mysql_get_host_info ( MYSQL* mysql )
+
+FUNCTION: c-string mysql_get_server_info ( MYSQL* mysql )
+
+FUNCTION: ulong mysql_get_server_version ( MYSQL* mysql )
+
+FUNCTION: uint mysql_get_proto_info ( MYSQL* mysql )
+
+FUNCTION: MYSQL_RES* mysql_list_dbs (
+    MYSQL* mysql,
+    c-string wild
+)
+
+FUNCTION: MYSQL_RES* mysql_list_tables (
+    MYSQL* mysql,
+    c-string wild
+)
+
+FUNCTION: MYSQL_RES* mysql_list_processes ( MYSQL* mysql )
+
+
+
+
+FUNCTION: MYSQL* mysql_real_connect (
+    MYSQL* mysql,
+    c-string host,
+    c-string user,
+    c-string passwd,
+    c-string db,
+    uint port,
+    c-string unix_socket,
+    ulong client_flag
+)
+
+FUNCTION: void mysql_close ( MYSQL* mysql )
+
+
+
+FUNCTION: bool mysql_commit ( MYSQL* mysql )
+
+FUNCTION: bool mysql_rollback ( MYSQL* mysql )
+
+FUNCTION: bool mysql_autocommit (
+    MYSQL* mysql,
+    bool auto_mode
+)
+
+FUNCTION: bool mysql_more_results ( MYSQL* mysql )
+
+FUNCTION: int mysql_next_result ( MYSQL* mysql )
+
+
+! <OLD-FUNCTIONS
+FUNCTION: MYSQL* mysql_connect (
+    MYSQL* mysql,
+    c-string host,
+    c-string user,
+    c-string passwd
+)
+
+FUNCTION: int mysql_create_db ( MYSQL* mysql, c-string db )
+
+FUNCTION: int mysql_drop_db ( MYSQL* mysql, c-string db )
+! OLD-FUNCTIONS>
+
+
+
+
+FUNCTION: int mysql_select_db ( MYSQL* mysql, c-string db )
+
+
+
+FUNCTION: int mysql_query ( MYSQL* mysql, c-string stmt_str )
+
+FUNCTION: int mysql_send_query (
+    MYSQL* mysql,
+    c-string stmt_str,
+    ulong length
+)
+
+FUNCTION: int mysql_real_query (
+    MYSQL* mysql,
+    c-string stmt_str,
+    ulong length
+)
+
+FUNCTION: MYSQL_RES* mysql_store_result ( MYSQL* mysql )
+
+FUNCTION: MYSQL_RES* mysql_use_result ( MYSQL* mysql )
+
+
+FUNCTION: int mysql_ping ( MYSQL* mysql )
+
+
+
+
+FUNCTION: ulonglong mysql_num_rows ( MYSQL_RES* mysql )
+
+FUNCTION: uint mysql_num_fields ( MYSQL_RES* mysql )
+
+FUNCTION: bool mysql_eof ( MYSQL_RES* result )
+
+FUNCTION: MYSQL_FIELD* mysql_fetch_field_direct (
+    MYSQL_RES* result,
+    uint fieldnr
+)
+
+FUNCTION: MYSQL_FIELD* mysql_fetch_fields ( MYSQL_RES* result )
+
+
+FUNCTION: uint mysql_field_count ( MYSQL* mysql )
+
+
+
+FUNCTION: MYSQL_ROW mysql_fetch_row ( MYSQL_RES* result )
+
+FUNCTION: MYSQL_FIELD* mysql_fetch_field ( MYSQL_RES* result )
+
+FUNCTION: void mysql_free_result ( MYSQL_RES* result )
+
+
+
+
+
+
+
+
+FUNCTION: MYSQL_STMT* mysql_stmt_init ( MYSQL* mysql )
+
+FUNCTION: int mysql_stmt_prepare (
+    MYSQL_STMT* stmt,
+    c-string query,
+    ulong length
+)
+
+FUNCTION: int mysql_stmt_execute ( MYSQL_STMT* stmt )
+
+FUNCTION: int mysql_stmt_fetch ( MYSQL_STMT* stmt )
+
+FUNCTION: int mysql_stmt_fetch_column (
+    MYSQL_STMT* stmt,
+    MYSQL_BIND* bind_arg,
+    uint column,
+    ulong offset
+)
+
+FUNCTION: int mysql_stmt_store_result ( MYSQL_STMT* stmt )
+
+FUNCTION: ulong mysql_stmt_param_count ( MYSQL_STMT* stmt )
+
+FUNCTION: bool mysql_stmt_attr_set (
+    MYSQL_STMT* stmt,
+    enum_stmt_attr_type attr_type,
+    void* attr
+)
+
+FUNCTION: bool mysql_stmt_attr_get (
+    MYSQL_STMT* stmt,
+    enum_stmt_attr_type attr_type,
+    void* attr
+)
+
+FUNCTION: bool mysql_stmt_bind_param (
+    MYSQL_STMT* stmt,
+    MYSQL_BIND* bnd
+)
+
+FUNCTION: bool mysql_stmt_bind_result (
+    MYSQL_STMT* stmt,
+    MYSQL_BIND* bnd
+)
+
+FUNCTION: bool mysql_stmt_close ( MYSQL_STMT* stmt )
+
+FUNCTION: bool mysql_stmt_reset ( MYSQL_STMT* stmt )
+
+FUNCTION: bool mysql_stmt_free_result ( MYSQL_STMT* stmt )
+
+FUNCTION: bool mysql_stmt_send_long_data (
+    MYSQL_STMT* stmt,
+    uint param_number,
+    c-string data,
+    ulong length
+)
+
+FUNCTION: MYSQL_RES* mysql_stmt_result_metadata ( MYSQL_STMT* stmt )
+
+FUNCTION: MYSQL_RES* mysql_stmt_param_metadata ( MYSQL_STMT* stmt )
+
+FUNCTION: uint mysql_stmt_errno ( MYSQL_STMT* stmt )
+
+FUNCTION: c-string mysql_stmt_error ( MYSQL_STMT* stmt )
+
+FUNCTION: c-string mysql_stmt_sqlstate ( MYSQL_STMT* stmt )
+
+FUNCTION: MYSQL_ROW_OFFSET mysql_stmt_row_seek (
+    MYSQL_STMT* stmt,
+    MYSQL_ROW_OFFSET offset
+)
+
+FUNCTION: MYSQL_ROW_OFFSET mysql_stmt_row_tell (
+    MYSQL_STMT* stmt,
+    MYSQL_ROW_OFFSET offset
+)
+
+FUNCTION: void mysql_stmt_data_seek (
+    MYSQL_STMT* stmt,
+    ulonglong offset
+)
+
+FUNCTION: ulonglong mysql_stmt_num_rows ( MYSQL_STMT* stmt )
+
+FUNCTION: ulonglong mysql_stmt_affected_rows ( MYSQL_STMT* stmt )
+
+FUNCTION: ulonglong mysql_stmt_insert_id ( MYSQL_STMT* stmt )
+
+FUNCTION: uint mysql_stmt_field_count ( MYSQL_STMT* stmt )
+
diff --git a/basis/mysql/db2/lib/authors.txt b/basis/mysql/db2/lib/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/mysql/db2/lib/lib.factor b/basis/mysql/db2/lib/lib.factor
new file mode 100644 (file)
index 0000000..a89c7e5
--- /dev/null
@@ -0,0 +1,204 @@
+! Copyright (C) 2010 John Benediktsson.
+! See http://factorcode.org/license.txt for BSD license.
+
+USING: accessors alien alien.c-types alien.data alien.strings
+calendar.format classes.struct combinators db2.errors db2.types
+fry generalizations io.encodings.utf8 kernel layouts locals
+make math math.parser mysql.db2.ffi present sequences serialize ;
+FROM: alien.c-types => short ;
+IN: mysql.db2.lib
+
+ERROR: mysql-error < db-error n string ;
+ERROR: mysql-sql-error < sql-error n string ;
+
+: mysql-check-result ( mysql n -- )
+    dup { 0 f } member? [ 2drop ] [
+        swap mysql_error mysql-error
+    ] if ;
+
+ERROR: mysql-connect-fail string mysql ;
+
+: mysql-check-connect ( mysql1 mysql2 -- )
+    dup net>> last_errno>> 0 = [
+        2drop
+    ] [
+        [ mysql_error ] dip mysql-connect-fail
+    ] if ;
+
+: mysql-stmt-check-result ( stmt n -- )
+    dup { 0 f } member? [ 2drop ] [
+        swap mysql_stmt_error mysql-error ! FIXME: mysql-sql-error
+    ] if ;
+
+:: mysql-connect ( host user passwd db port -- mysql/f )
+    f mysql_init :> mysql
+    mysql host user passwd db port f 0 mysql_real_connect :> handle
+    mysql dup mysql-check-connect handle ;
+
+: mysql-#rows ( result -- n )
+    mysql_num_rows ;
+
+: mysql-#columns ( result -- n )
+    mysql_num_fields ;
+
+: mysql-next ( result -- ? )
+    mysql_fetch_row ;
+
+
+: mysql-column ( result n -- value )
+    swap [ cell * ] [ current_row>> ] bi* <displaced-alien>
+    void* deref utf8 alien>string ;
+
+: mysql-row ( result -- seq )
+    [ current_row>> ] [ mysql-#columns ] bi [
+        [ void* deref utf8 alien>string ]
+        [ cell swap <displaced-alien> ] bi swap
+    ] replicate nip ;
+
+! returns a result or f
+: mysql-query ( mysql query -- result/f )
+    dupd mysql_query dupd mysql-check-result mysql_store_result ;
+
+! Throws if fails
+: mysql-command ( mysql query -- )
+    dupd mysql_query mysql-check-result ;
+
+: mysql-reset-statement ( statement -- )
+    handle>> dup mysql_stmt_reset mysql-stmt-check-result ;
+
+: mysql-free-statement ( statement -- )
+    handle>> dup mysql_stmt_free_result mysql-stmt-check-result ;
+
+: mysql-free-result ( result -- )
+    handle>> mysql_free_result ;
+
+
+: <mysql-time> ( timestamp -- MYSQL_TIME )
+    MYSQL_TIME <struct>
+        over year>> >>year
+        over month>> >>month
+        over day>> >>day
+        over hour>> >>hour
+        over minute>> >>minute
+        swap second>> >>second ;
+
+:: <mysql-bind> ( index key value type -- mysql_BIND )
+    MYSQL_BIND <struct>
+        index >>param_number
+        value type {
+            { INTEGER [ MYSQL_TYPE_LONG ] }
+            { BIG-INTEGER [ MYSQL_TYPE_LONGLONG ] }
+            { SIGNED-BIG-INTEGER [ MYSQL_TYPE_LONGLONG ] }
+            { UNSIGNED-BIG-INTEGER [ MYSQL_TYPE_LONGLONG ] }
+            { BOOLEAN [ MYSQL_TYPE_BIT ] }
+            { TEXT [ MYSQL_TYPE_VARCHAR ] }
+            { VARCHAR [ MYSQL_TYPE_VARCHAR ] }
+            { DOUBLE [ MYSQL_TYPE_DOUBLE ] }
+            { DATE [ timestamp>ymd MYSQL_TYPE_DATE ] }
+            { TIME [ timestamp>hms MYSQL_TYPE_TIME ] }
+            { DATETIME [ timestamp>ymdhms MYSQL_TYPE_DATETIME ] }
+            { TIMESTAMP [ timestamp>ymdhms MYSQL_TYPE_DATETIME ] }
+            { BLOB [ MYSQL_TYPE_BLOB ] }
+            { FACTOR-BLOB [ object>bytes MYSQL_TYPE_BLOB ] }
+            { URL [ present MYSQL_TYPE_VARCHAR ] }
+            { +db-assigned-key+ [ MYSQL_TYPE_LONG ] }
+            { +random-key+ [ MYSQL_TYPE_LONGLONG ] }
+            { NULL [ MYSQL_TYPE_NULL ] }
+            [ no-sql-type ]
+        } case >>buffer_type >>buffer
+        ! FIXME: buffer_length
+        ! FIXME: is_null
+    ;
+
+
+
+
+<PRIVATE
+
+CONSTANT: MIN_CHAR -255
+CONSTANT: MAX_CHAR 256
+
+CONSTANT: MIN_SHORT -65535
+CONSTANT: MAX_SHORT 65536
+
+CONSTANT: MIN_INT -4294967295
+CONSTANT: MAX_INT 4294967296
+
+CONSTANT: MIN_LONG -18446744073709551615
+CONSTANT: MAX_LONG 18446744073709551616
+
+FROM: alien.c-types => short ;
+
+: fixnum>c-ptr ( n -- c-ptr )
+    dup 0 < [ abs 1 + ] when {
+        { [ dup MAX_CHAR  <= ] [ char <ref> ] }
+        { [ dup MAX_SHORT <= ] [ short <ref> ] }
+        { [ dup MAX_INT   <= ] [ int <ref> ] }
+        { [ dup MAX_LONG  <= ] [ longlong <ref> ] }
+        [ "too big" throw ]
+    } cond ;
+
+PRIVATE>
+
+
+! : mysql-stmt-query ( stmt -- result )
+!     dup mysql_stmt_execute dupd mysql-stmt-check-result
+!     mysql_stmt_store_result ;
+
+
+: mysql-column-typed ( result n -- value )
+    [ mysql-column ] [ mysql_fetch_field_direct ] 2bi type>> {
+        { MYSQL_TYPE_DECIMAL  [ string>number ] }
+        { MYSQL_TYPE_SHORT    [ string>number ] }
+        { MYSQL_TYPE_LONG     [ string>number ] }
+        { MYSQL_TYPE_FLOAT    [ string>number ] }
+        { MYSQL_TYPE_DOUBLE   [ string>number ] }
+        { MYSQL_TYPE_LONGLONG [ string>number ] }
+        { MYSQL_TYPE_INT24    [ string>number ] }
+        [ drop ]
+    } case ;
+
+
+
+
+: create-db ( mysql db -- )
+    dupd mysql_create_db mysql-check-result ;
+
+: drop-db ( mysql db -- )
+    dupd mysql_drop_db mysql-check-result ;
+
+: select-db ( mysql db -- )
+    dupd mysql_select_db mysql-check-result ;
+
+<PRIVATE
+
+: cols ( result n -- cols )
+    [ dup mysql_fetch_field name>> ] replicate nip ;
+
+: row ( result n -- row/f )
+    swap mysql_fetch_row [
+        swap [
+            [ void* deref utf8 alien>string ]
+            [ cell swap <displaced-alien> ] bi swap
+        ] replicate nip
+    ] [ drop f ] if* ;
+
+: rows ( result n -- rows )
+    [ '[ _ _ row dup ] [ , ] while drop ] { } make ;
+
+PRIVATE>
+
+: list-dbs ( mysql -- seq )
+    f mysql_list_dbs dup mysql_num_fields rows concat ;
+
+: list-tables ( mysql -- seq )
+    f mysql_list_tables dup mysql_num_fields rows concat ;
+
+: list-processes ( mysql -- seq )
+    mysql_list_processes dup mysql_num_fields rows ;
+
+: query-db ( mysql sql -- cols rows )
+    mysql-query [
+        dup mysql_num_fields [ cols ] [ rows ] 2bi
+    ] [ mysql_free_result ] bi ;
+
diff --git a/basis/mysql/db2/result-sets/authors.txt b/basis/mysql/db2/result-sets/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/mysql/db2/result-sets/result-sets.factor b/basis/mysql/db2/result-sets/result-sets.factor
new file mode 100644 (file)
index 0000000..b922e0e
--- /dev/null
@@ -0,0 +1,104 @@
+! Copyright (C) 2011 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors alien.c-types alien.data classes.struct
+combinators db2.result-sets destructors kernel locals
+mysql.db2.connections mysql.db2.ffi mysql.db2.lib libc
+specialized-arrays sequences ;
+IN: mysql.db2.result-sets
+
+SPECIALIZED-ARRAY: MYSQL_BIND
+SPECIALIZED-ARRAY: bool
+SPECIALIZED-ARRAY: ulong
+
+TUPLE: mysql-result-set < result-set bind #columns nulls lengths errors ;
+
+M: mysql-result-set dispose ( result-set -- )
+    ! the handle is a stmt handle here, not a result_set handle
+    [ mysql-free-statement ]
+    [ f >>handle drop ] bi ;
+
+M: mysql-result-set #columns ( result-set -- n ) #columns>> ;
+
+M: mysql-result-set advance-row ( result-set -- ) drop ;
+
+M: mysql-result-set column
+    B
+    3drop f
+    ;
+
+M: mysql-result-set more-rows? ( result-set -- ? )
+    handle>> [
+        mysql_stmt_fetch {
+            { 0 [ t ] }
+            { MYSQL_NO_DATA [ f ] }
+            { MYSQL_DATA_TRUNCATED [ "truncated, bailing out.." throw ] }
+        } case
+    ] [
+        f
+    ] if* ;
+
+
+! Reference: http://dev.mysql.com/doc/refman/5.6/en/mysql-stmt-fetch.html
+M:: mysql-db-connection statement>result-set ( statement -- result-set )
+    statement handle>> :> handle
+    [
+        ! 0 int <ref> malloc-byte-array |free :> buffer0
+        256 malloc :> buffer0
+        256 :> buffer_length0
+        0 ulong <ref> malloc-byte-array |free :> length0
+        f bool <ref> malloc-byte-array |free :> error0
+        f bool <ref> malloc-byte-array |free :> is_null0
+
+        handle mysql_stmt_execute
+        [ handle ] dip mysql-stmt-check-result
+
+        statement handle \ mysql-result-set new-result-set :> result-set
+
+        handle mysql_stmt_result_metadata :> metadata
+        metadata field_count>> :> #columns
+
+        #columns MYSQL_BIND malloc-array |free :> binds
+        #columns ulong malloc-array |free :> lengths
+        #columns bool malloc-array |free :> is_nulls
+        #columns bool malloc-array |free :> errors
+
+        binds [
+            MYSQL_TYPE_STRING >>buffer_type
+            256 malloc >>buffer
+            256 >>buffer_length
+            is_null0 >>is_null
+            length0 >>length
+            error0 >>error
+        ] map drop
+        
+
+
+        MYSQL_BIND malloc-struct |free
+            ! MYSQL_TYPE_LONG >>buffer_type
+            MYSQL_TYPE_STRING >>buffer_type
+            buffer0 >>buffer
+            buffer_length0 >>buffer_length
+            is_null0 >>is_null
+            length0 >>length
+            error0 >>error
+        :> bind0
+
+
+        bind0 result-set bind<<
+        
+        handle bind0 mysql_stmt_bind_result
+            f = [ handle mysql_stmt_error throw ] unless
+        handle mysql_stmt_store_result
+            0 = [ "mysql store_result error" throw ] unless
+
+        ! handle mysql_stmt_fetch .
+        ! bind0 buffer>> alien>native-string .
+
+        ! handle mysql_stmt_fetch .
+        ! bind0 buffer>> alien>native-string .
+
+        result-set
+    ] with-destructors
+    ;
+    ! TODO: bind data here before more-rows? calls mysql_stmt_fetch
+
diff --git a/basis/mysql/db2/statements/authors.txt b/basis/mysql/db2/statements/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/mysql/db2/statements/statements.factor b/basis/mysql/db2/statements/statements.factor
new file mode 100644 (file)
index 0000000..fe71752
--- /dev/null
@@ -0,0 +1,35 @@
+! Copyright (C) 2011 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors db2.connections db2.statements
+io.encodings.string io.encodings.utf8 kernel
+mysql.db2.connections mysql.db2.ffi mysql.db2.lib namespaces
+sequences locals ;
+IN: mysql.db2.statements
+
+:: mysql-prepare ( stmt sql -- stmt )
+    stmt sql utf8 encode dup length mysql_stmt_prepare
+    [ stmt ] dip mysql-stmt-check-result stmt ;
+
+: mysql-maybe-prepare ( statement -- statement )
+    dup handle>> [
+        db-connection get handle>> mysql_stmt_init
+        over sql>> mysql-prepare >>handle
+    ] unless ;
+
+M: mysql-db-connection prepare-statement*
+    mysql-maybe-prepare ;
+
+M: mysql-db-connection bind-sequence
+    drop ;
+
+M: mysql-db-connection reset-statement
+    [ handle>> mysql-reset-statement ] keep ;
+
+M: mysql-db-connection dispose-statement
+    f >>handle drop ;
+
+! M: mysql-db-connection next-bind-index "?" ;
+
+! M: mysql-db-connection init-bind-index ;
+
+
diff --git a/basis/mysql/mysql.factor b/basis/mysql/mysql.factor
new file mode 100644 (file)
index 0000000..70af374
--- /dev/null
@@ -0,0 +1,9 @@
+! Copyright (C) 2010 John Benediktsson.
+! See http://factorcode.org/license.txt for BSD license.
+USING: kernel sequences vocabs vocabs.loader ;
+IN: mysql
+
+[
+    "mysql.db2"
+    "mysql.orm"
+] [ require ] each
diff --git a/basis/mysql/orm/authors.txt b/basis/mysql/orm/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/mysql/orm/orm.factor b/basis/mysql/orm/orm.factor
new file mode 100644 (file)
index 0000000..8137707
--- /dev/null
@@ -0,0 +1,5 @@
+! Copyright (C) 2011 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: ;
+IN: mysql.orm
+
diff --git a/basis/orm/authors.txt b/basis/orm/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/orm/binders/authors.txt b/basis/orm/binders/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/orm/binders/binders.factor b/basis/orm/binders/binders.factor
new file mode 100644 (file)
index 0000000..aab2a50
--- /dev/null
@@ -0,0 +1,10 @@
+! Copyright (C) 2010 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: constructors ;
+IN: orm.binders
+
+TUPLE: column-binder-in column value ;
+CONSTRUCTOR: <column-binder-in> column-binder-in ( column value -- obj ) ;
+
+TUPLE: column-binder-out column ;
+CONSTRUCTOR: <column-binder-out> column-binder-out ( column -- obj ) ;
diff --git a/basis/orm/examples/authors.txt b/basis/orm/examples/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/orm/examples/examples.factor b/basis/orm/examples/examples.factor
new file mode 100644 (file)
index 0000000..ed773d9
--- /dev/null
@@ -0,0 +1,127 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors calendar constructors db2.types orm
+orm.persistent sequences multiline ;
+IN: orm.examples
+
+TUPLE: user id name age ;
+CONSTRUCTOR: <user> user ( name age -- obj ) ;
+TUPLE: score id user score ;
+
+PERSISTENT: user
+    { "id" +db-assigned-key+ }
+    { "name" VARCHAR }
+    { "age" INTEGER } ;
+
+PERSISTENT: score
+    { "id" +db-assigned-key+ }
+    { "user" user }
+    { "score" INTEGER } ;
+
+
+TUPLE: user2 id name age ;
+CONSTRUCTOR: <user2> user2 ( name age -- obj ) ;
+TUPLE: score2 id user score ;
+
+PERSISTENT: user2
+    { "id" INTEGER +primary-key+ }
+    { "name" VARCHAR }
+    { "age" INTEGER } ;
+
+PERSISTENT: score2
+    { "id" INTEGER +primary-key+ }
+    { "user" user2 }
+    { "score" INTEGER } ;
+
+
+TUPLE: user3 id name age ;
+CONSTRUCTOR: <user3> user3 ( name age -- obj ) ;
+TUPLE: score3 id user score ;
+
+PERSISTENT: user3
+    { "id" INTEGER +primary-key+ }
+    { "name" VARCHAR NOT-NULL }
+    { "age" INTEGER NOT-NULL } ;
+
+PERSISTENT: score3
+    { "id" INTEGER +primary-key+ }
+    { "user" user3 NOT-NULL }
+    { "score" INTEGER NOT-NULL } ;
+
+
+/*
+
+T{ score2
+    {
+        user
+        T{ user { "name" "erg" } }
+    }
+} select-tuples
+
+{
+    T{ score
+        { id 0 }
+        { user T{ user { id 0 } { name "erg" } { age 28 } } }
+        { score 100 }
+    }
+    T{ score
+        { id 1 }
+        { user T{ user { id 0 } { name "erg" } { age 28 } } }
+        { score 106 }
+    }
+}
+*/
+
+TUPLE: jar id name beans ;
+TUPLE: bean id ;
+
+PERSISTENT: bean
+    { "id" INTEGER +primary-key+ } ;
+
+PERSISTENT: jar
+    { "id" INTEGER +primary-key+ }
+    { "name" VARCHAR }
+    { "beans" { bean sequence } } ;
+
+/*
+
+T{ bean } select-tuples
+{
+    T{ bean { id 1 } }
+    T{ bean { id 2 } }
+    T{ bean { id 3 } }
+    T{ bean { id 4 } }
+}
+
+T{ jar } select-tuples
+{
+    T{ jar { id 1 } { "beans1" }
+        { beans { T{ bean { id 1 } } T{ bean { id 2 } } } }
+    }
+    T{ jar { id 2 } { "beans2" }
+        { beans { T{ bean { id 3 } } T{ bean { id 4 } } } }
+    }
+}
+
+T{ jar { beans IGNORE } } select-tuples
+{
+    T{ jar { id 1 } { "beans1" } }
+    T{ jar { id 2 } { "beans2" } }
+}
+
+*/
+
+! Blogs
+TUPLE: entity id author date content ;
+CONSTRUCTOR: <entity> entity ( author content -- entity ) now >>date ;
+PERSISTENT: entity
+    { "id" +db-assigned-key+ }
+    { "author" VARCHAR NOT-NULL } ! uid
+    { "date" TIMESTAMP NOT-NULL }
+    { "content" TEXT NOT-NULL } ;
+
+TUPLE: post < entity title comments ;
+CONSTRUCTOR: <post> post ( title -- entity ) now >>date ;
+PERSISTENT: post
+    { "title" VARCHAR NOT-NULL }
+    { "comments" { entity sequence } } ;
diff --git a/basis/orm/orm.factor b/basis/orm/orm.factor
new file mode 100644 (file)
index 0000000..c2fef50
--- /dev/null
@@ -0,0 +1,96 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors arrays assocs classes classes.tuple
+combinators db2.query-objects db2.types fry kernel make
+namespaces orm.persistent sequences shuffle db2.utils
+locals vocabs multiline ;
+IN: orm
+
+USE: vocabs.loader
+"orm.query-objects" require
+
+
+
+/*
+
+TUPLE: relations internal external ;
+
+SYMBOL: table-counter
+
+: (tuple>relations) ( n tuple -- )
+    [ ] [ >persistent columns>> ] bi [
+        dup relation-category [
+            2dup getter>> call( obj -- obj' ) dup IGNORE = [
+                4drop
+            ] [
+                [ dup relation-class new ] unless*
+                over relation-category [
+                    swap [
+                        [
+                            [ class swap 2array ]
+                            [ relation-class table-counter [ inc ] [ get ] bi 2array ] bi*
+                        ] dip 3array ,
+                    ] dip
+                    [ table-counter get ] dip (tuple>relations)
+                ] [
+                    4drop
+                ] if*
+            ] if
+        ] [
+            3drop
+        ] if
+    ] with with each ;
+
+: tuple>relations ( tuple -- seq )
+    0 table-counter [
+        [ 0 swap (tuple>relations) ] { } make
+    ] with-variable ;
+
+: internal-class-relations ( class -- seq )
+    dup >persistent columns>> [
+        type>> dup tuple-class? [ 2array ] [ 2drop f ] if
+    ] with filter ;
+
+: external-class-relations ( class -- seq )
+    [ inherited-persistent-table get-global ] dip
+    '[
+        nip columns>> [ type>> _ eq? ] any?
+    ] assoc-filter ;
+
+: class-relations ( class -- internal external )
+    [ internal-class-relations ]
+    [ external-class-relations ] bi ;
+
+! Don't introspect the quotation at runtime.
+
+: column-contains-many? ( column -- ? )
+    type>> dup array? [
+        ?first2 [ tuple-class? ] [ sequence = ] bi* and
+    ] [
+        drop f
+    ] if ;
+
+: find-contains-many ( class -- seq )
+    >persistent columns>> [ column-contains-many? ] filter ;
+
+: find-one:one ( class -- seq )
+    ;
+
+: class>relations ( class -- relation )
+    >persistent columns>> [ tuple-class? ] filter ;
+
+
+: relation>join ( triple -- seq )
+    
+    ;
+
+: relations>joins ( seq -- seq' )
+    [ relation>join ] map concat ;
+
+: tuple>select-statement ( tuple -- select )
+    [ select new ] dip
+    {
+        ! [ tuple>relations relations>joins 1array >>join ]
+        ! [ ]
+    } cleave ;
+*/
diff --git a/basis/orm/persistent/authors.txt b/basis/orm/persistent/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/orm/persistent/persistent-tests.factor b/basis/orm/persistent/persistent-tests.factor
new file mode 100644 (file)
index 0000000..cab1222
--- /dev/null
@@ -0,0 +1,3 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+IN: orm.persistent.tests
diff --git a/basis/orm/persistent/persistent.factor b/basis/orm/persistent/persistent.factor
new file mode 100644 (file)
index 0000000..49b0684
--- /dev/null
@@ -0,0 +1,454 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors annotations arrays assocs classes
+classes.tuple combinators combinators.short-circuit
+constructors db2.types db2.utils kernel math namespaces
+parser quotations sequences sets strings words make
+fry lexer db2.binders random multiline ;
+QUALIFIED-WITH: namespaces n
+IN: orm.persistent
+
+ERROR: bad-table-name obj ;
+ERROR: bad-type-modifier obj ;
+ERROR: not-persistent obj ;
+ERROR: duplicate-persistent-columns obj ;
+
+SYMBOL: raw-persistent-table
+SYMBOL: inherited-persistent-table
+
+raw-persistent-table [ H{ } clone ] initialize
+inherited-persistent-table [ H{ } clone ] initialize
+
+<PRIVATE
+
+GENERIC: parse-table-name ( object -- class table )
+GENERIC: parse-name ( object -- accessor column )
+GENERIC: parse-column-type ( object -- string )
+GENERIC: parse-column-modifiers ( object -- string )
+GENERIC: lookup-raw-persistent ( obj -- obj' )
+
+PRIVATE>
+
+GENERIC: >persistent ( obj -- persistent )
+
+SYMBOL: deferred-persistent
+
+: ?>persistent ( class -- persistent/f )
+    raw-persistent-table get ?at [ drop f ] unless ;
+
+: >persistent* ( class -- persistent/f )
+    raw-persistent-table get ?at [ not-persistent ] unless ;
+
+: check-sql-name ( string -- string )
+    [ ] [ ] [ sql-name-replace ] tri = [ bad-table-name ] unless ;
+
+TUPLE: persistent class table-name columns primary-key incomplete? ;
+
+CONSTRUCTOR: <persistent> persistent ( class table-name columns -- obj ) ;
+
+TUPLE: db-column persistent
+slot-name column-name type modifiers getter setter generator ;
+
+GENERIC: compute-generator ( tuple type -- quotation/f )
+
+M: object compute-generator 2drop f ;
+
+M: +random-key+ compute-generator
+    2drop [ drop 32 random-bits ] ;
+
+: set-generator ( tuple -- tuple )
+    [ dup type>> compute-generator ] [ generator<< ] [ ] tri ;
+
+: <db-column> ( slot-name column-name type modifiers -- obj )
+    db-column new
+        swap ??1array >>modifiers
+        swap >>type
+        swap >>column-name
+        swap >>slot-name
+        set-generator ; inline
+
+: ?cut ( seq n -- before after ) [ short head ] [ short tail ] 2bi ;
+
+ERROR: db-column-must-be-triple extra ;
+: parse-column ( seq -- db-column )
+    3 ?cut [ db-column-must-be-triple ] unless-empty
+    ?first3
+    [ parse-name ]
+    [ parse-column-type ]
+    [ parse-column-modifiers ] tri* <db-column> ;
+
+: superclass-persistent-columns ( class -- columns )
+    superclasses-of [ ?>persistent ] map
+    sift \ deferred-persistent swap remove
+    [ columns>> ] map concat ;
+
+: join-persistent-hierarchy ( class -- persistent )
+    [ superclass-persistent-columns [ clone ] map ]
+    [ >persistent* clone ] bi
+    [ columns<< ] keep ;
+
+: compute-persistent-slots ( persistent -- )
+    dup columns>>
+    [ [ clone ] change-persistent ] map
+    [ persistent<< ] with each ;
+
+: compute-setters ( persistent -- )
+    columns>> [
+        dup slot-name>>
+        [ lookup-getter 1quotation >>getter ]
+        [ lookup-setter 1quotation >>setter ] bi drop
+    ] each ;
+
+: column-primary-key? ( column -- ? )
+    {
+        [ type>> sql-primary-key? ]
+        [ modifiers>> [ sql-primary-key? ] any? ]
+    } 1|| ;
+
+GENERIC: table-name* ( column -- string )
+
+M: sequence table-name* first table-name* ;
+
+M: db-column table-name* persistent>> table-name>> ;
+
+M: tuple-class table-name* >persistent table-name>> ;
+
+M: tuple table-name* >persistent table-name>> ;
+
+M: in-binder table-name* table-name>> ;
+
+M: out-binder table-name* table-name>> ;
+
+: table-name ( obj -- string )
+    table-name* ;
+
+: quoted-table-name ( obj -- string )
+    table-name* "\"" dup surround ;
+
+GENERIC: find-primary-key ( obj -- seq )
+
+M: persistent find-primary-key ( persistent -- seq )
+    columns>> [ column-primary-key? ] filter ;
+
+M: tuple-class find-primary-key ( class -- seq )
+    >persistent primary-key>> ;
+
+M: tuple find-primary-key ( class -- seq )
+    class-of find-primary-key ;
+
+: db-assigned-key? ( persistent -- ? )
+     find-primary-key [
+        {
+            [ type>> +db-assigned-key+ = ]
+            [ modifiers>> +db-assigned-key+ swap member? ]
+        } 1||
+    ] all? ;
+
+: user-assigned-key? ( class -- ? )
+    find-primary-key [ modifiers>> +primary-key+ swap member? ] all? ;
+
+: compute-primary-key ( persistent -- )
+    dup find-primary-key >>primary-key drop ;
+
+: primary-key-slots ( obj -- seq )
+    >persistent
+    find-primary-key [ [ table-name ] [ slot-name>> ] bi "." glue ] map ;
+
+: remove-primary-key ( slots -- slots' )
+    [ type>> sql-primary-key? not ] filter ;
+    ! [ modifiers>> +primary-key+ swap member? not ] filter ;
+
+: process-persistent ( persistent -- persistent )
+    {
+        [ compute-persistent-slots ]
+        [ compute-setters ]
+        [ compute-primary-key ]
+        [ ]
+    } cleave ;
+
+: check-columns ( persistent -- persistent )
+    dup columns>> [ column-name>> ] map all-unique?
+    [ duplicate-persistent-columns ] unless ;
+
+M: persistent lookup-raw-persistent ;
+M: tuple lookup-raw-persistent class-of lookup-raw-persistent ;
+M: tuple-class lookup-raw-persistent raw-persistent-table get at ;
+
+M: persistent >persistent ;
+
+M: tuple >persistent class-of >persistent ;
+
+M: tuple-class >persistent
+    ! inherited-persistent-table get [
+        join-persistent-hierarchy
+        process-persistent
+        check-columns ;
+    ! ] cache ;
+
+: ensure-persistent ( obj -- obj )
+    dup lookup-raw-persistent [ not-persistent ] unless ;
+
+: ensure-type ( obj -- obj )
+    dup tuple-class? [ ensure-persistent ] [ ensure-sql-type ] if ;
+
+: ensure-type-modifier ( obj -- obj )
+    {
+        { [ dup { sequence } member? ] [ ] }
+        { [ dup integer? ] [ ] }
+        [ bad-type-modifier ]
+    } cond ;
+
+: clear-persistent ( -- )
+    inherited-persistent-table get clear-assoc ;
+
+: rebuild-persistent ( -- )
+    clear-persistent
+    raw-persistent-table get
+    [ deferred-persistent = [ >persistent ] unless drop ] assoc-each ;
+
+: save-persistent ( persistent -- )
+    dup class>> raw-persistent-table get set-at ;
+
+: make-persistent ( class name columns -- )
+    <persistent> save-persistent
+    rebuild-persistent ;
+
+SYNTAX: PERSISTENT:
+    scan-object parse-table-name check-sql-name
+    \ ; parse-until
+    [ parse-column ] map make-persistent ;
+
+! SYNTAX: RECONSTRUCTOR:
+    ! scan scan-object
+    ! [ >persistent ] [ >>reconstructor drop ] bi* ;
+
+SYNTAX: DEFER-PERSISTENT:
+    \ deferred-persistent scan-object
+    raw-persistent-table get ?at [
+        2drop
+    ] [
+        raw-persistent-table get set-at
+    ] if ;
+
+M: integer parse-table-name throw ;
+
+M: sequence parse-table-name
+    unclip swap
+    unclip swap
+    [ ] [ "." join ] bi* [ "." glue ] unless-empty ;
+
+M: tuple-class parse-table-name
+    dup name>> sql-name-replace ;
+
+M: sequence parse-name
+    2 ensure-length first2
+    [ ensure-string ] bi@ sql-name-replace ;
+
+M: string parse-name dup 2array parse-name ;
+
+M: word parse-column-type ensure-type ;
+
+M: sequence parse-column-type
+    2 ensure-length first2
+    [ ensure-type ] [ ensure-type-modifier ] bi* 2array ;
+
+M: word parse-column-modifiers ensure-sql-modifier ;
+
+M: sequence parse-column-modifiers
+    [ ensure-sql-modifier ] map ;
+
+
+
+
+
+
+SYMBOL: table-names
+
+SINGLETONS: one:one one:many many:one many:many ;
+
+ERROR: bad-relation-category obj ;
+ERROR: bad-relation-class obj ;
+
+
+GENERIC: relation-category? ( obj -- ? )
+
+M: sequence relation-category?
+    dup length {
+        { 1 [ first relation-category? ] }
+        { 2 [ first relation-category? ] }
+        [ drop bad-relation-category ]
+    } case ;
+
+M: db-column relation-category? type>> relation-category? ;
+
+M: tuple-class relation-category? drop t ;
+
+M: word relation-category? drop f ;
+
+: relation-columns ( obj -- columns )
+    >persistent
+    columns>> [ type>> relation-category? ] filter ;
+
+
+
+GENERIC: relation-category ( obj -- obj' )
+
+M: db-column relation-category
+    type>> relation-category ;
+
+M: object relation-category drop f ;
+M: tuple-class relation-category drop one:one ;
+
+M: sequence relation-category
+    dup length {
+        { 1 [ first relation-category ] }
+        { 2 [ first2 sequence = [ drop one:many ] [ bad-relation-category ] if ] }
+        [ drop bad-relation-category ]
+    } case ;
+
+
+
+GENERIC: relation-class* ( obj -- obj' )
+
+: relation-class ( column -- obj )
+    type>> relation-class* ;
+
+M: tuple-class relation-class* ;
+
+M: sequence relation-class*
+    dup length {
+        { 0 [ bad-relation-class ] }
+        [ drop first ]
+    } case ;
+
+M: object relation-class* drop f ;
+
+
+: query-shape ( class -- seq )
+    >persistent columns>> [ dup relation-category ] { } map>assoc ;
+
+: filter-persistent ( quot -- seq )
+    [ raw-persistent-table get values ] dip filter ; inline
+
+: map-persistent ( quot -- seq )
+    [ raw-persistent-table get values ] dip { } map-as ; inline
+
+: each-persistent ( quot -- )
+    [ raw-persistent-table get values ] dip each ; inline
+
+: find-many:many-relations ( class -- seq )
+    sequence 2array
+    '[
+        columns>> [ type>> _ = ] filter empty? not
+    ] filter-persistent ;
+
+GENERIC: select-columns* ( obj -- )
+
+M: persistent select-columns*
+    columns>> [ select-columns* ] each ;
+
+M: db-column select-columns*
+    dup type>> {
+        { [ dup tuple-class? ] [ nip >persistent select-columns* ] }
+        [ drop , ]
+    } cond ;
+
+: select-columns ( obj -- seq )
+    [ select-columns* ] { } make ;
+
+
+
+SYMBOL: seq
+SYMBOL: n
+
+GENERIC: select-reconstructor* ( obj -- )
+
+M: persistent select-reconstructor*
+    columns>> [ select-reconstructor* ] each ;
+
+M: db-column select-reconstructor*
+    dup relation-category {
+        { one:one [
+            [ type>> >persistent select-reconstructor* ]
+            [ setter>> , ] bi
+        ] }
+        { one:many [
+            [ relation-class >persistent select-reconstructor* ]
+            [ getter>> '[ over _ push ] , ] bi
+        ] }
+        [ drop n get n inc , seq , \ get , \ nth , setter>> % ]
+    } case ;
+
+: select-reconstructor ( obj -- seq )
+    [
+        0 n n:set
+        { 1 2 3 4 5 } seq n:set
+        [ select-reconstructor* ] [ ] make
+    ] with-scope ;
+
+: ((column>create-text)) ( db-column -- )
+    {
+        [ type>> sql-create-type>string % ]
+        [ modifiers>> [ " " % sql-modifiers>string % ] when* ]
+    } cleave ;
+
+: (column>create-text) ( db-column -- string )
+    [
+        [ slot-name>> sql-name-replace % " " % ]
+        [ ((column>create-text)) ] bi
+    ] "" make ;
+
+: (columns>create-text) ( seq -- seq )
+    [ (column>create-text) ] map sift ;
+
+: columns>create-text ( seq -- string )
+    (columns>create-text) ", " join ;
+
+: class>foreign-key-create ( class -- string )
+    [ table-name ] [ find-primary-key (columns>create-text) ] bi
+    [ "_" glue ] with map ", " join ;
+
+: class>primary-key-create ( class -- string )
+    find-primary-key [
+        f
+    ] [
+        [ column-name>> ] map "," join
+        ", primary key(" ")" surround
+    ] if-empty ;
+
+: column>create-text ( db-column -- string )
+    dup relation-category {
+        { one:one [ relation-class class>foreign-key-create ] }
+        { one:many [ drop f ] }
+        { many:one [ relation-class class>foreign-key-create ] }
+        { many:many [ drop f ] }
+        { f [ (column>create-text) ] }
+        [ bad-relation-category ]
+    } case ;
+
+: find-one:many-columns ( obj -- seq ) >persistent class>> '[
+    columns>> [ [ relation-class _ = ] [ relation-category one:many =
+    ] bi and ] filter ] map-persistent concat ;
+
+: class>one:many-relations ( class -- string )
+    find-one:many-columns
+    [ persistent>> class>> class>foreign-key-create ] map ", " join ;
+
+: set-primary-key ( tuple obj -- tuple' )
+    over find-primary-key 1 ensure-length
+    first setter>> call( tuple obj -- tuple ) ;
+
+/*
+: select-joins ( obj -- seq )
+    query-shape
+    [ nip ] assoc-filter
+    [
+        {
+            [ first relation-class table-name ]
+            [ first relation-class table-name ]
+            [ first persistent>> primary-key-slots ]
+            [ first relation-class table-name ]
+            [ first relation-class primary-key-slots ]
+        } cleave <left-join>
+    ] map ;
+*/
diff --git a/basis/orm/queries/authors.txt b/basis/orm/queries/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/orm/queries/queries-tests.factor b/basis/orm/queries/queries-tests.factor
new file mode 100644 (file)
index 0000000..440cdf3
--- /dev/null
@@ -0,0 +1,10 @@
+! Copyright (C) 2010 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: continuations db2.debug orm.examples orm.queries
+orm.tuples tools.test ;
+IN: orm.queries.tests
+
+[ [ \ user drop-table ] ignore-errors ] test-dbs
+
+[ \ user create-table ] test-dbs
+[ \ user drop-table ] test-dbs
diff --git a/basis/orm/queries/queries.factor b/basis/orm/queries/queries.factor
new file mode 100644 (file)
index 0000000..c7ed193
--- /dev/null
@@ -0,0 +1,151 @@
+! Copyright (C) 2010 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors assocs combinators db2 db2.binders
+db2.connections db2.statements db2.types db2.utils fry kernel
+locals make orm.persistent sequences reconstructors arrays
+orm.binders db2.query-objects ;
+IN: orm.queries
+
+HOOK: create-table-sql db-connection ( tuple-class -- object )
+HOOK: ensure-table-sql db-connection ( tuple-class -- object )
+HOOK: drop-table-sql db-connection ( tuple-class -- object )
+
+HOOK: insert-db-assigned-key-sql db-connection ( tuple -- object )
+HOOK: insert-user-assigned-key-sql db-connection ( tuple -- object )
+HOOK: insert-tuple-set-key db-connection ( tuple statement -- )
+HOOK: update-tuple-sql db-connection ( tuple -- object )
+HOOK: delete-tuple-sql db-connection ( tuple -- object )
+HOOK: select-tuple-sql db-connection ( tuple -- object )
+
+ERROR: can't-reconstruct query ;
+
+: set-reconstructor ( query -- query )
+    ! { { bag >>id } { bean >>id >>bag-id >>color } } rows>tuples
+    dup from>> length 1 = [ can't-reconstruct ] unless
+    {
+        [ from>> first class>> 1array ]
+        [
+            out>> [ column>> setter>> first ] map append 1array
+            '[ _ rows>tuples concat ]
+        ]
+        [ reconstructor<< ]
+        [ ]
+    } cleave ;
+
+HOOK: n>bind-sequence db-connection ( n -- sequence ) 
+HOOK: continue-bind-sequence db-connection ( previous n -- sequence )
+
+: n>bind-string ( n -- string ) n>bind-sequence "," join ;
+M: object n>bind-sequence "?" <repetition> ;
+M: object continue-bind-sequence nip "?" <repetition> ;
+
+M: object create-table-sql
+    >persistent dup table-name>> quote-sql-name
+    [
+        [
+            [ columns>> ] dip
+            "CREATE TABLE " % %
+            "(" % [ ", " % ] [
+                [ column-name>> % " " % ]
+                [ type>> sql-create-type>string % ]
+                [ modifiers>> " " join % ] tri
+            ] interleave
+        ] [
+            drop
+            find-primary-key [
+                ", " %
+                "PRIMARY KEY(" %
+                [ "," % ] [ column-name>> % ] interleave
+                ")" %
+            ] unless-empty
+            ");" %
+        ] 2bi
+    ] "" make ;
+
+M: object drop-table-sql
+    >persistent table-name>> quote-sql-name
+    "DROP TABLE " ";" surround ;
+
+: columns>in-binders ( columns tuple -- sequence )
+    '[
+        [ _ swap getter>> ( obj -- slot-value ) call-effect ]
+        [ type>> ] bi
+        <in-binder-low>
+    ] { } map-as ;
+
+M:: object delete-tuple-sql ( tuple -- statement )
+    <statement> :> statement
+    tuple >persistent :> persistent
+
+    statement
+        persistent table-name>> "DELETE FROM " prepend add-sql
+        persistent find-primary-key :> columns:primary-key
+        columns:primary-key length :> #primary-key
+
+        " WHERE " add-sql
+        columns:primary-key tuple columns>in-binders add-in
+
+        columns:primary-key [ column-name>> ] map
+        #primary-key n>bind-sequence zip
+            [ " = " glue ] { } assoc>map ", " join add-sql ;
+
+: call-generators ( columns tuple -- )
+    '[
+        _
+        2dup swap getter>> call( obj -- obj ) [
+            2drop
+        ] [
+            over generator>> [
+                dupd call( obj -- obj )
+                rot setter>> call( obj obj -- obj ) drop
+            ] [
+                2drop
+            ] if*
+        ] if
+    ] each ;
+
+! XXX: include the assoc-filter?
+: filter-tuple-values ( persistent tuple -- assoc )
+    [ columns>> ] dip
+    2dup call-generators
+    '[ _ over getter>> call( obj -- slot-value ) ] { } map>assoc ;
+
+: filter-empty-tuple-values ( persistent tuple -- assoc )
+    filter-tuple-values
+    [ nip ] assoc-filter ;
+
+! : where-primary-key ( statement persistent tuple -- statement )
+    ! [ find-primary-key ] dip
+    ! [ columns>in-binders add-in ]
+    ! [ drop [ column-name>> ] map " WHERE " prepend add-sql ] 2bi ;
+
+M:: object update-tuple-sql ( tuple -- statement )
+    <statement> :> statement
+    tuple >persistent :> persistent
+
+    statement
+        persistent table-name>> "UPDATE " " SET " surround add-sql
+        persistent columns>> remove-primary-key :> columns:no-primary-key
+        persistent find-primary-key :> columns:primary-key
+        columns:no-primary-key length :> #columns
+        columns:no-primary-key length :> #primary-key
+
+        columns:no-primary-key [ column-name>> ] map
+        #columns n>bind-sequence zip [ " = " glue ] { } assoc>map ", " join add-sql
+
+        columns:no-primary-key tuple columns>in-binders add-in
+        " WHERE " add-sql
+        columns:primary-key tuple columns>in-binders add-in
+
+        columns:primary-key [ column-name>> ] map
+        #columns #primary-key continue-bind-sequence zip [ " = " glue ] { } assoc>map ", " join add-sql ;
+
+
+M: object select-tuple-sql ( tuple -- object )
+    [ <select> ] dip
+    [ >persistent ] [ ] bi {
+        [ filter-empty-tuple-values [ first2 <column-binder-in> ] map >>in ]
+        [ drop columns>> [ <column-binder-out> ] map >>out ]
+        [ drop 1array >>from ]
+    } 2cleave ;
+
diff --git a/basis/orm/query-objects/authors.txt b/basis/orm/query-objects/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/orm/query-objects/query-objects.factor b/basis/orm/query-objects/query-objects.factor
new file mode 100644 (file)
index 0000000..d756a0a
--- /dev/null
@@ -0,0 +1,23 @@
+! Copyright (C) 2010 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors db2.query-objects kernel orm.binders sequences
+orm.persistent ;
+IN: orm.query-objects
+
+: column-binder>qualified-column-name ( column-binder -- string )
+    column>>
+    [ persistent>> table-name>> ] [ column-name>> ] bi "." glue ;
+
+M: column-binder-out >qualified-column-name
+    column-binder>qualified-column-name ;
+
+M: column-binder-in >qualified-column-name
+    column-binder>qualified-column-name ;
+
+M: persistent >table-as table-name>> ;
+
+
+M: column-binder-in binder-operator
+    drop " = " ;
+
+M: column-binder-in flatten-binder ;
diff --git a/basis/orm/tuples/authors.txt b/basis/orm/tuples/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/orm/tuples/tuples-tests.factor b/basis/orm/tuples/tuples-tests.factor
new file mode 100644 (file)
index 0000000..5473ea8
--- /dev/null
@@ -0,0 +1,149 @@
+! Copyright (C) 2010 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: db2 db2.debug db2.types debugger
+kernel orm.persistent orm.tuples sequences
+tools.test ;
+IN: orm.tuples.tests
+
+TUPLE: foo-1 a b ;
+
+PERSISTENT: foo-1
+{ "a" INTEGER +primary-key+ }
+{ "b" VARCHAR } ;
+
+: test-1 ( -- )
+    [ "drop table foo_1" sql-command ] try
+
+    [ ]
+    [ "create table foo_1 (a integer primary key, b varchar)" sql-command ] unit-test
+
+    [ ]
+    [ 1 "lol" foo-1 boa insert-tuple ] unit-test
+
+    [ { { "1" "lol" } } ]
+    [ "select * from foo_1" sql-query ] unit-test
+
+    [ ]
+    [ 1 "omg" foo-1 boa update-tuple ] unit-test
+
+    [ { { "1" "omg" } } ]
+    [ "select * from foo_1" sql-query ] unit-test
+
+    [ { { "1" "omg" } } ]
+    [ "select * from foo_1" sql-query ] unit-test
+
+    [ { T{ foo-1 { a 1 } { b "omg" } } } ]
+    [ T{ foo-1 } select-tuples ] unit-test
+
+    [ ] [ 1 f foo-1 boa delete-tuples ] unit-test
+
+    [ { } ] [ "select * from foo_1" sql-query ] unit-test
+    [ { } ] [ T{ foo-1 } select-tuples ] unit-test
+
+    [ ] [ 1 "lol" foo-1 boa insert-tuple ] unit-test
+
+    [ { T{ foo-1 { a 1 } { b "lol" } } } ]
+    [ T{ foo-1 f 1 } select-tuples ] unit-test
+
+    [ { T{ foo-1 { a 1 } { b "lol" } } } ]
+    [ T{ foo-1 f f "lol" } select-tuples ] unit-test
+
+    [ { T{ foo-1 { a 1 } { b "lol" } } } ]
+    [ T{ foo-1 f 1 "lol" } select-tuples ] unit-test
+    ;
+
+[ test-1 ] test-dbs
+
+TUPLE: foo-2 id a ;
+PERSISTENT: foo-2
+{ "id" INTEGER +primary-key+ }
+{ "a" VARCHAR } ;
+
+TUPLE: bar-2 id b ;
+PERSISTENT: bar-2
+{ "id" INTEGER +primary-key+ }
+{ "b" { foo-2 sequence } } ;
+
+: setup-test-2-sql ( -- )
+    [ "drop table foo_2" sql-command ] try
+    [ "drop table bar_2" sql-command ] try
+
+    [ ] [ "create table foo_2(id integer primary key, a varchar, bar_2_id integer)" sql-command ] unit-test
+    [ ] [ "create table bar_2(id integer primary key)" sql-command ] unit-test
+
+    [ ] [ "insert into foo_2(id, a, bar_2_id) values(0, 'first', 0);" sql-command ] unit-test
+    [ ] [ "insert into foo_2(id, a, bar_2_id) values(1, 'second', 0);" sql-command ] unit-test
+
+    [ ] [ "insert into bar_2(id) values(0);" sql-command ] unit-test
+
+    [
+        {
+            { "0" "0" "first" }
+            { "0" "1" "second" }
+        }
+    ] [ "select bar_2.id, foo_2.id, foo_2.a from bar_2 left join foo_2 on foo_2.bar_2_id = bar_2.id where bar_2.id = 0" sql-query ] unit-test
+
+    ;
+
+: test-2 ( -- )
+    setup-test-2-sql
+
+    ! [ ] [ T{ bar-2 f 0 } select-tuples ] unit-test
+    ;
+
+[ setup-test-2-sql ] test-dbs
+
+[ test-2 ] test-dbs
+
+
+TUPLE: foo-3 id a ;
+PERSISTENT: foo-3
+{ "id" INTEGER +primary-key+ }
+{ "a" VARCHAR } ;
+
+TUPLE: bar-3 id b ;
+PERSISTENT: bar-3
+{ "id" INTEGER +primary-key+ }
+{ "b" { foo-3 sequence } } ;
+
+TUPLE: baz-3 id c ;
+PERSISTENT: baz-3
+{ "id" INTEGER +primary-key+ }
+{ "c" { bar-3 sequence } } ;
+
+: setup-test-3-sql ( -- )
+    [ "drop table foo_3" sql-command ] try
+    [ "drop table bar_3" sql-command ] try
+    [ "drop table baz_3" sql-command ] try
+
+    [ ] [ "create table foo_3(id integer primary key, a varchar, bar_3_id integer)" sql-command ] unit-test
+    [ ] [ "create table bar_3(id integer primary key, baz_3_id integer)" sql-command ] unit-test
+    [ ] [ "create table baz_3(id integer primary key)" sql-command ] unit-test
+
+    [ ] [ "insert into foo_3(id, a, bar_3_id) values(0, 'first', 0);" sql-command ] unit-test
+    [ ] [ "insert into foo_3(id, a, bar_3_id) values(1, 'second', 0);" sql-command ] unit-test
+
+    [ ] [ "insert into foo_3(id, a, bar_3_id) values(2, 'third', 1);" sql-command ] unit-test
+    [ ] [ "insert into foo_3(id, a, bar_3_id) values(3, 'fourth', 1);" sql-command ] unit-test
+
+    [ ] [ "insert into bar_3(id, baz_3_id) values(0, 0);" sql-command ] unit-test
+    [ ] [ "insert into bar_3(id, baz_3_id) values(1, 0);" sql-command ] unit-test
+
+    [ ] [ "insert into baz_3(id) values(0);" sql-command ] unit-test
+
+    [
+        {
+            { "0" "0" "0" "first" }
+            { "0" "0" "1" "second" }
+            { "0" "1" "2" "third" }
+            { "0" "1" "3" "fourth" }
+        }
+    ] [ "select baz_3.id, bar_3.id, foo_3.id, foo_3.a
+            from baz_3
+                left join bar_3 on baz_3.id = bar_3.baz_3_id
+                left join foo_3 on bar_3.id = foo_3.bar_3_id
+                where baz_3.id = 0 order by baz_3.id, bar_3.id, foo_3.id"
+        sql-query
+    ] unit-test ;
+
+[ setup-test-3-sql ] test-dbs
diff --git a/basis/orm/tuples/tuples.factor b/basis/orm/tuples/tuples.factor
new file mode 100644 (file)
index 0000000..efd6108
--- /dev/null
@@ -0,0 +1,209 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors arrays assocs combinators combinators.smart db2
+db2.binders db2.errors db2.query-objects db2.types db2.utils fry
+kernel macros make math math.parser mirrors namespaces
+orm orm.persistent orm.queries sequences sets
+splitting.monotonic destructors multiline ;
+IN: orm.tuples
+
+: create-table ( tuple-class -- )
+    create-table-sql sql-command ;
+
+: drop-table ( tuple-class -- )
+    drop-table-sql sql-command ;
+
+: ensure-table ( tuple-class -- )
+    ensure-persistent
+    '[ [ _ create-table ] ignore-table-exists ] ignore-function-exists ;
+
+: ensure-tables ( classes -- ) [ ensure-table ] each ;
+
+: recreate-table ( tuple-class -- )
+    ensure-persistent
+    [
+        '[
+            [
+                _ drop-table-sql sql-command
+            ] ignore-table-missing
+        ] ignore-function-missing
+    ] [ create-table ] bi ;
+
+: recreate-tables ( tuple-classes -- )
+    [ recreate-table ] each ;
+
+: insert-tuple ( tuple -- )
+    dup db-assigned-key? [
+        dup insert-db-assigned-key-sql insert-tuple-set-key
+    ] [
+        insert-user-assigned-key-sql sql-command
+    ] if ;
+
+: update-tuple ( tuple -- )
+    update-tuple-sql sql-command ;
+
+: select-tuples ( tuple -- seq )
+    select-tuple-sql set-reconstructor sql-query ;
+
+: select-tuple ( tuple -- elt/f )
+    select-tuples ?first ;
+
+: count-tuples ( tuple -- n )
+    select-tuples length ;
+
+: delete-tuples ( tuple -- )
+    delete-tuple-sql sql-command ;
+
+
+
+/*
+
+
+: tuple>pairs ( tuple -- seq )
+    [ >persistent columns>> ] [ <mirror> >alist ] bi
+    [ first2 dup IGNORE = [ 3drop f ] [ nip 2array ] if ] 2map sift ;
+
+GENERIC# pair>binder* 1 ( binder pair -- binder )
+
+: (pair>binder) ( binder pair -- binder )
+    {
+        [ first persistent>> class>> >>class ]
+        [
+            first
+            [ persistent>> table-name>> "0" ]
+            [ column-name>> ] bi <table-ordinal-column> >>toc
+        ]
+        [ first type>> >>type ]
+    } cleave ;
+
+M: in-binder pair>binder* ( binder-class pair -- binder )
+    [ (pair>binder) ] [ second >>value ] bi ;
+
+M: out-binder pair>binder* ( binder-class pair -- binder )
+    (pair>binder) ;
+
+: pair>binder ( pair binder-class -- binder ) new swap pair>binder* ;
+
+: tuple>binders ( tuple binder -- seq )
+    [ tuple>pairs ] dip '[ _ pair>binder ] map ;
+
+: insert-tuple ( tuple -- )
+    [ <insert> ] dip
+    in-binder tuple>binders >>in
+    query-object>statement sql-command ;
+
+
+: tuple>primary-key-binders ( tuple -- seq )
+    [ find-primary-key ] keep '[
+        dup slot-name>> _ get-slot-named
+        2array equal-binder pair>binder
+    ] map ;
+
+
+: update-tuple ( tuple -- )
+    [ <update> ] dip
+    {
+        [ equal-binder tuple>binders >>in ]
+        [ tuple>primary-key-binders >>where ]
+    } cleave
+    query-object>statement sql-command ;
+
+
+: delete-tuples ( tuple -- )
+    [ <delete> ] dip
+    tuple>primary-key-binders >>where
+    query-object>statement sql-command ;
+
+ERROR: no-setter ;
+
+: out-binder>setter ( toc -- word )
+    [ class>> >persistent columns>> ]
+    [ toc>> column-name>> ] bi '[ column-name>> _ = ] find
+    nip [ no-setter ] unless* setter>> ;
+
+MACRO: query-object>reconstructor ( tuple -- quot )
+B
+    out>> [ [ class>> ] bi@ = ] monotonic-split
+    [ [ first class>> ] [ [ out-binder>setter ] map ] bi ] { } map>assoc 
+    [
+        [
+            first2
+            [ , \ new , ]
+            [ reverse [ \ swap , , (( obj obj -- obj )) , \ call-effect , ] each ] bi*
+        ] each
+    ] [ ] make '[ [ _ input<sequence ] ] ;
+
+SYMBOL: ordinal
+
+: next-ordinal ( -- string )
+    ordinal [ dup 1 + ] change number>string ;
+
+: (select-tuples) ( tuple -- tuple )
+    0 ordinal [
+        [ <select> ] dip {
+            [ out-binder tuple>binders >>out ]
+            [ equal-binder tuple>binders [ value>> ] filter >>in ]
+            [
+                tuple>pairs [ first persistent>> table-name>> ] map members
+                [ next-ordinal <table-ordinal> ] map >>from
+            ]
+        } cleave
+    ] with-variable ;
+
+MACRO: select-tuples ( tuple -- tuples )
+    (select-tuples)
+    [ query-object>statement sql-query ] keep
+B
+    query-object>reconstructor
+    '[ [ @ ] map ] ;
+
+: reconstruct ( seq quot tuple -- seq' )
+    2drop
+    ;
+
+! : select-tuple ( tuple -- tuple/f )
+    ! [ (select-tuples) 1 >>limit sql-query ] [ make-reconstructor ] [ ] tri reconstruct ;
+
+: count-tuples ( tuple -- n )
+    ;
+*/
+
+
+/*
+TUPLE: foo a b ;
+
+PERSISTENT: foo
+{ "a" INTEGER +primary-key+ }
+{ "b" VARCHAR } ;
+[ [ "drop table foo" sql-command ] test-sqlite ] try
+[ "create table foo (a integer primary key, b varchar)" sql-command ] test-sqlite
+[ 1 "lol" foo boa insert-tuple ] test-sqlite
+[ "select * from foo" sql-query . ] test-sqlite
+[ "update foo set a=1, b='omg' where a=1" sql-command ] test-sqlite
+[ "select * from foo" sql-query . ] test-sqlite
+[ 1 f foo boa (select-tuples) query-object>statement ] test-sqlite
+*/
+
+
+/*
+ERROR: unimplemented ;
+
+: select-relations ( tuple relations -- seq )
+    unimplemented
+    drop
+    ;
+
+: select-no-relations ( tuple -- seq )
+    
+    ;
+
+
+
+: select-tuples2 ( tuple -- seq )
+    dup tuple>relations [
+        select-no-relations
+    ] [
+        select-relations
+    ] if-empty ;
+
+*/
diff --git a/basis/postgresql/authors.txt b/basis/postgresql/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/postgresql/db2/authors.txt b/basis/postgresql/db2/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/postgresql/db2/connections/authors.txt b/basis/postgresql/db2/connections/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/postgresql/db2/connections/connections-tests.factor b/basis/postgresql/db2/connections/connections-tests.factor
new file mode 100644 (file)
index 0000000..16e00fa
--- /dev/null
@@ -0,0 +1,8 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: postgresql.db2.connections db2.debug tools.test ;
+IN: postgresql.db2.connections.tests
+
+[ ] [
+    [ ] test-postgresql
+] unit-test
diff --git a/basis/postgresql/db2/connections/connections.factor b/basis/postgresql/db2/connections/connections.factor
new file mode 100644 (file)
index 0000000..a5debdc
--- /dev/null
@@ -0,0 +1,42 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors combinators db2.connections db2.errors
+postgresql.db2 postgresql.db2.errors postgresql.db2.ffi
+postgresql.db2.lib kernel sequences splitting destructors ;
+IN: postgresql.db2.connections
+
+<PRIVATE
+
+TUPLE: postgresql-db-connection < db-connection ;
+
+: <postgresql-db-connection> ( handle -- db-connection )
+    \ postgresql-db-connection new-db-connection ;
+
+PRIVATE>
+
+M: postgresql-db db>db-connection-generic ( db -- db-connection )
+    {
+        [ host>> ]
+        [ port>> ]
+        [ pgopts>> ]
+        [ pgtty>> ]
+        [ database>> ]
+        [ username>> ]
+        [ password>> ]
+    } cleave connect-postgres <postgresql-db-connection> ;
+
+M: postgresql-db-connection dispose* ( db-connection -- )
+    [ handle>> PQfinish ] [ f >>handle drop ] bi ;
+
+ERROR: postgresql:sql-error string length ;
+
+M: postgresql-db-connection parse-sql-error
+    "\n" split dup length {
+        { 1 [ first parse-postgresql-sql-error ] }
+        { 3 [
+                first3
+                [ parse-postgresql-sql-error ] 2dip
+                postgresql-location >>location
+        ] }
+        [ postgresql:sql-error ]
+    } case ;
diff --git a/basis/postgresql/db2/db2.factor b/basis/postgresql/db2/db2.factor
new file mode 100644 (file)
index 0000000..a01f979
--- /dev/null
@@ -0,0 +1,24 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: kernel sequences vocabs.loader vocabs ;
+IN: postgresql.db2
+
+TUPLE: postgresql-db
+    host port pgopts pgtty database username password ;
+
+: <postgresql-db> ( -- postgresql-db )
+    postgresql-db new ; inline
+
+{
+    "postgresql.db2.connections"
+    "postgresql.db2.errors"
+    "postgresql.db2.ffi"
+    "postgresql.db2.lib"
+    "postgresql.db2.result-sets"
+    "postgresql.db2.statements"
+    "postgresql.db2.types"
+    "postgresql.db2.queries"
+    ! "postgresql.db2.introspection"
+
+    "postgresql.orm"
+} [ require ] each
diff --git a/basis/postgresql/db2/errors/authors.txt b/basis/postgresql/db2/errors/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/postgresql/db2/errors/errors.factor b/basis/postgresql/db2/errors/errors.factor
new file mode 100644 (file)
index 0000000..d03bcd0
--- /dev/null
@@ -0,0 +1,52 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors db2.errors kernel locals.types math math.parser
+multiline peg.ebnf sequences strings ;
+IN: postgresql.db2.errors
+
+EBNF: parse-postgresql-sql-error [=[
+
+Error = "ERROR:" [ ]+
+
+TableError =
+    Error ("relation "|"table ")(!(" already exists").)+:table " already exists"
+        => [[ table >string unquote <sql-table-exists> ]]
+    | Error ("relation "|"table ")(!(" does not exist").)+:table " does not exist"
+        => [[ table >string unquote <sql-table-missing> ]]
+
+FunctionError =
+    Error "function" (!(" already exists").)+:table " already exists"
+        => [[ table >string <sql-function-exists> ]]
+    | Error "function" (!(" does not exist").)+:table " does not exist"
+        => [[ table >string <sql-function-missing> ]]
+
+SyntaxError =
+    Error "syntax error at end of input":error
+        => [[ error >string <sql-syntax-error> ]]
+    | Error "syntax error at or near " .+:syntaxerror
+        => [[ syntaxerror >string unquote <sql-syntax-error> ]]
+
+UnknownError = .* => [[ >string <sql-unknown-error> ]]
+
+PostgresqlSqlError = (TableError | FunctionError | SyntaxError | UnknownError)
+
+]=]
+
+TUPLE: parse-postgresql-location column line text ;
+C: <parse-postgresql-location> parse-postgresql-location
+
+EBNF: parse-postgresql-line-error [=[
+
+Line = "LINE " [0-9]+:line ": " .+:sql
+    => [[ f line >string string>number sql >string <parse-postgresql-location> ]]
+
+]=]
+
+:: set-caret-position ( error caret-line -- error )
+    caret-line length
+    error line>> number>string length "LINE : " length +
+    - [ error ] dip >>column ;
+
+: postgresql-location ( line column -- obj )
+    [ parse-postgresql-line-error ] dip
+    set-caret-position ;
diff --git a/basis/postgresql/db2/ffi/authors.txt b/basis/postgresql/db2/ffi/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/postgresql/db2/ffi/ffi.factor b/basis/postgresql/db2/ffi/ffi.factor
new file mode 100644 (file)
index 0000000..113e227
--- /dev/null
@@ -0,0 +1,369 @@
+! Copyright (C) 2007, 2008 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+! tested on debian linux with postgresql 8.1
+USING: alien alien.c-types alien.syntax combinators system
+alien.libraries ;
+IN: postgresql.db2.ffi
+
+<< "postgresql" {
+    { [ os windows? ]  [ "libpq.dll" ] }
+    { [ os macosx? ] [ "libpq.dylib" ] }
+    { [ os unix?  ]  [ "libpq.so" ] }
+} cond cdecl add-library >>
+
+! ConnSatusType
+CONSTANT: CONNECTION_OK                     0
+CONSTANT: CONNECTION_BAD                    1
+CONSTANT: CONNECTION_STARTED                2
+CONSTANT: CONNECTION_MADE                   3
+CONSTANT: CONNECTION_AWAITING_RESPONSE      4
+CONSTANT: CONNECTION_AUTH_OK                5
+CONSTANT: CONNECTION_SETENV                 6
+CONSTANT: CONNECTION_SSL_STARTUP            7
+CONSTANT: CONNECTION_NEEDED                 8
+
+! PostgresPollingStatusType
+CONSTANT: PGRES_POLLING_FAILED              0
+CONSTANT: PGRES_POLLING_READING             1
+CONSTANT: PGRES_POLLING_WRITING             2
+CONSTANT: PGRES_POLLING_OK                  3
+CONSTANT: PGRES_POLLING_ACTIVE              4
+
+! ExecStatusType;
+CONSTANT: PGRES_EMPTY_QUERY                 0
+CONSTANT: PGRES_COMMAND_OK                  1
+CONSTANT: PGRES_TUPLES_OK                   2
+CONSTANT: PGRES_COPY_OUT                    3
+CONSTANT: PGRES_COPY_IN                     4
+CONSTANT: PGRES_BAD_RESPONSE                5
+CONSTANT: PGRES_NONFATAL_ERROR              6
+CONSTANT: PGRES_FATAL_ERROR                 7
+
+! PGTransactionStatusType;
+CONSTANT: PQTRANS_IDLE                      0
+CONSTANT: PQTRANS_ACTIVE                    1
+CONSTANT: PQTRANS_INTRANS                   2
+CONSTANT: PQTRANS_INERROR                   3
+CONSTANT: PQTRANS_UNKNOWN                   4
+
+! PGVerbosity;
+CONSTANT: PQERRORS_TERSE                    0
+CONSTANT: PQERRORS_DEFAULT                  1
+CONSTANT: PQERRORS_VERBOSE                  2
+
+CONSTANT: InvalidOid 0
+
+TYPEDEF: int ConnStatusType
+TYPEDEF: int ExecStatusType 
+TYPEDEF: int PostgresPollingStatusType
+TYPEDEF: int PGTransactionStatusType 
+TYPEDEF: int PGVerbosity 
+
+C-TYPE: PGconn
+C-TYPE: PGresult
+C-TYPE: PGcancel
+TYPEDEF: uint Oid
+TYPEDEF: char pqbool
+C-TYPE: PQconninfoOption
+C-TYPE: PGnotify
+C-TYPE: PQArgBlock
+C-TYPE: PQprintOpt
+C-TYPE: SSL
+C-TYPE: FILE
+
+LIBRARY: postgresql
+
+! Exported functions of libpq
+
+! make a new client connection to the backend
+! Asynchronous (non-blocking)
+FUNCTION: PGconn* PQconnectStart ( c-string conninfo )
+FUNCTION: PostgresPollingStatusType PQconnectPoll ( PGconn* conn )
+
+! Synchronous (blocking)
+FUNCTION: PGconn* PQconnectdb ( c-string conninfo )
+FUNCTION: PGconn* PQsetdbLogin ( c-string pghost, c-string pgport,
+             c-string pgoptions, c-string pgtty,
+             c-string dbName,
+             c-string login, c-string 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 )
+
+! 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: c-string PQdb ( PGconn* conn )
+FUNCTION: c-string PQuser ( PGconn* conn )
+FUNCTION: c-string PQpass ( PGconn* conn )
+FUNCTION: c-string PQhost ( PGconn* conn )
+FUNCTION: c-string PQport ( PGconn* conn )
+FUNCTION: c-string PQtty ( PGconn* conn )
+FUNCTION: c-string PQoptions ( PGconn* conn )
+FUNCTION: ConnStatusType PQstatus ( PGconn* conn )
+FUNCTION: PGTransactionStatusType PQtransactionStatus ( PGconn* conn )
+FUNCTION: c-string PQparameterStatus ( PGconn* conn,
+                  c-string paramName )
+FUNCTION: int PQprotocolVersion ( PGconn* conn )
+! FUNCTION: int PQServerVersion ( PGconn* conn )
+FUNCTION: c-string PQerrorMessage ( PGconn* conn )
+FUNCTION: int PQsocket ( PGconn* conn )
+FUNCTION: int PQbackendPID ( PGconn* conn )
+FUNCTION: int PQclientEncoding ( PGconn* conn )
+FUNCTION: int PQsetClientEncoding ( PGconn* conn, c-string 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, c-string 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, c-string query )
+FUNCTION: PGresult* PQexecParams ( PGconn* conn,
+             c-string command,
+             int nParams,
+             Oid* paramTypes,
+             c-string* paramValues,
+             int* paramLengths,
+             int* paramFormats,
+             int resultFormat )
+FUNCTION: PGresult* PQprepare ( PGconn* conn, c-string stmtName,
+        c-string query, int nParams,
+        Oid* paramTypes )
+FUNCTION: PGresult* PQexecPrepared ( PGconn* conn,
+             c-string stmtName,
+             int nParams,
+             c-string* paramValues,
+             int* paramLengths,
+             int* paramFormats,
+             int resultFormat )
+
+! Interface for multiple-result or asynchronous queries
+FUNCTION: int PQsendQuery ( PGconn* conn, c-string query )
+FUNCTION: int PQsendQueryParams ( PGconn* conn,
+                  c-string command,
+                  int nParams,
+                  Oid* paramTypes,
+                  c-string* paramValues,
+                  int* paramLengths,
+                  int* paramFormats,
+                  int resultFormat )
+FUNCTION: PGresult* PQsendPrepare ( PGconn* conn, c-string stmtName,
+            c-string query, int nParams,
+            Oid* paramTypes )
+FUNCTION: int PQsendQueryPrepared ( PGconn* conn,
+                  c-string stmtName,
+                  int nParams,
+                  c-string* 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, c-string buffer, int nbytes )
+FUNCTION: int    PQputCopyEnd ( PGconn* conn, c-string errormsg )
+FUNCTION: int    PQgetCopyData ( PGconn* conn, c-string* buffer, int async )
+
+! Deprecated routines for copy in/out
+FUNCTION: int    PQgetline ( PGconn* conn, c-string string, int length )
+FUNCTION: int    PQputline ( PGconn* conn, c-string string )
+FUNCTION: int    PQgetlineAsync ( PGconn* conn, c-string buffer, int bufsize )
+FUNCTION: int    PQputnbytes ( PGconn* conn, c-string 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: c-string PQresStatus ( ExecStatusType status )
+FUNCTION: c-string PQresultErrorMessage ( PGresult* res )
+FUNCTION: c-string PQresultErrorField ( PGresult* res, int fieldcode )
+FUNCTION: int   PQntuples ( PGresult* res )
+FUNCTION: int   PQnfields ( PGresult* res )
+FUNCTION: int   PQbinaryTuples ( PGresult* res )
+FUNCTION: c-string PQfname ( PGresult* res, int field_num )
+FUNCTION: int   PQfnumber ( PGresult* res, c-string 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: c-string PQcmdStatus ( PGresult* res )
+FUNCTION: c-string PQoidStatus ( PGresult* res )
+FUNCTION: Oid   PQoidValue ( PGresult* res )
+FUNCTION: c-string PQcmdTuples ( PGresult* res )
+! FUNCTION: c-string PQgetvalue ( PGresult* res, int tup_num, int field_num )
+FUNCTION: void* 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 ( ptr -- ) 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,
+                                    c-string to, c-string from, size_t length,
+                                    int* error )
+FUNCTION: c-string PQescapeByteaConn ( PGconn* conn,
+                                    c-string from, size_t length,
+                                    size_t* to_length )
+FUNCTION: void* PQunescapeBytea ( c-string strtext, size_t* retbuflen )
+! FUNCTION: c-string PQunescapeBytea ( c-string strtext, size_t* retbuflen )
+! These forms are deprecated!
+FUNCTION: size_t PQescapeString ( void* to, c-string from, size_t length )
+FUNCTION: c-string PQescapeBytea ( c-string 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,
+                                c-string 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, c-string buf, size_t len )
+FUNCTION: int    lo_write ( PGconn* conn, int fd, c-string 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, c-string filename )
+FUNCTION: int    lo_export ( PGconn* conn, Oid lobjId, c-string filename )
+
+! === in fe-misc.c ===
+
+! Determine length of multibyte encoded char at *s
+FUNCTION: int    PQmblen ( c-string s, int encoding )
+
+! Determine display length of multibyte encoded char at *s
+FUNCTION: int    PQdsplen ( c-string s, int encoding )
+
+! Get encoding id from environment variable PGCLIENTENCODING
+FUNCTION: int    PQenv2encoding ( )
+
+! From git, include/catalog/pg_type.h
+CONSTANT: BOOL-OID 16
+CONSTANT: BYTEA-OID 17
+CONSTANT: CHAR-OID 18
+CONSTANT: NAME-OID 19
+CONSTANT: INT8-OID 20
+CONSTANT: INT2-OID 21
+CONSTANT: INT4-OID 23
+CONSTANT: TEXT-OID 23
+CONSTANT: OID-OID 26
+CONSTANT: FLOAT4-OID 700
+CONSTANT: FLOAT8-OID 701
+CONSTANT: VARCHAR-OID 1043
+CONSTANT: DATE-OID 1082
+CONSTANT: TIME-OID 1083
+CONSTANT: TIMESTAMP-OID 1114
+CONSTANT: TIMESTAMPTZ-OID 1184
+CONSTANT: INTERVAL-OID 1186
+CONSTANT: NUMERIC-OID 1700
+
diff --git a/basis/postgresql/db2/introspection/authors.txt b/basis/postgresql/db2/introspection/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/postgresql/db2/introspection/introspection.factor b/basis/postgresql/db2/introspection/introspection.factor
new file mode 100644 (file)
index 0000000..5572c4c
--- /dev/null
@@ -0,0 +1,124 @@
+! Copyright (C) 2011 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors db2.introspection db2.types kernel orm.persistent
+orm.tuples postgresql.db2.connections.private ;
+IN: postgresql.db2.introspection
+
+TUPLE: postgresql-object schemaname tablename tableowner tablespace
+hasindexes hasrules hastriggers ;
+
+PERSISTENT: { postgresql-object "pg_tables" }
+    { "schemaname" TEXT }
+    { "tablename" TEXT }
+    { "tableowner" TEXT }
+    { "tablespace" TEXT }
+    { "hasindexes" BOOLEAN }
+    { "hasrules" BOOLEAN }
+    { "hastriggers" BOOLEAN } ;
+
+TUPLE: information-schema-column
+    table_catalog
+    table_schema
+    table_name
+    column_name
+    ordinal_position
+    column_default
+    is_nullable
+    data_type
+    character_maximum_length
+    character_octet_length
+    numeric_precision
+    numeric_precision_radix
+    numeric_scale
+    datetime_precision
+    interval_type
+    interval_precision
+    character_set_catalog
+    character_set_schema
+    character_set_name
+    collation_catalog
+    collation_schema
+    collation_name
+    domain_catalog
+    domain_schema
+    domain_name
+    udt_catalog
+    udt_schema
+    udt_name
+    scope_catalog
+    scope_schema
+    scope_name
+    maximum_cardinality
+    dtd_identifier
+    is_self_referencing
+    is_identity
+    identity_generation
+    identity_start
+    identity_increment
+    identity_maximum
+    identity_minimum
+    identity_cycle
+    is_generated
+    generation_expression
+    is_updatable ;
+
+PERSISTENT: { information-schema-column "information_schema" "columns" }
+    { "table_catalog" VARCHAR }
+    { "table_schema" VARCHAR }
+    { "table_name" VARCHAR }
+    { "column_name" VARCHAR }
+    { "ordinal_position" INTEGER }
+    { "column_default" CHARACTER }
+    { "is_nullable" CHARACTER }
+    { "data_type" CHARACTER }
+    { "character_maximum_length" INTEGER }
+    { "character_octet_length" INTEGER }
+    { "numeric_precision" INTEGER }
+    { "numeric_precision_radix" INTEGER }
+    { "numeric_scale" INTEGER }
+    { "datetime_precision" INTEGER }
+    { "interval_type" CHARACTER }
+    { "interval_precision" CHARACTER }
+    { "character_set_catalog" VARCHAR }
+    { "character_set_schema" VARCHAR }
+    { "character_set_name" VARCHAR }
+    { "collation_catalog" VARCHAR }
+    { "collation_schema" VARCHAR }
+    { "collation_name" VARCHAR }
+    { "domain_catalog" VARCHAR }
+    { "domain_schema" VARCHAR }
+    { "domain_name" VARCHAR }
+    { "udt_catalog" VARCHAR }
+    { "udt_schema" VARCHAR }
+    { "udt_name" VARCHAR }
+    { "scope_catalog" VARCHAR }
+    { "scope_schema" VARCHAR }
+    { "scope_name" VARCHAR }
+    { "maximum_cardinality" INTEGER }
+    { "dtd_identifier" VARCHAR }
+    { "is_self_referencing" CHARACTER }
+    { "is_identity" CHARACTER }
+    { "identity_generation" CHARACTER }
+    { "identity_start" CHARACTER }
+    { "identity_increment" CHARACTER }
+    { "identity_maximum" CHARACTER }
+    { "identity_minimum" CHARACTER }
+    { "identity_cycle" CHARACTER }
+    { "is_generated" CHARACTER }
+    { "generation_expression" CHARACTER }
+    { "is_updatable" CHARACTER } ;
+    
+
+M: postgresql-db-connection all-tables
+    postgresql-object new select-tuples ;
+
+M: postgresql-db-connection table-columns
+    information-schema-column new
+        swap >>table_name
+    select-tuples ;
+
+! M: postgresql-db-connection all-db-objects
+
+! M: postgresql-db-connection all-indices
+
+! M: postgresql-db-connection temporary-db-objects
diff --git a/basis/postgresql/db2/lib/authors.txt b/basis/postgresql/db2/lib/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/postgresql/db2/lib/lib.factor b/basis/postgresql/db2/lib/lib.factor
new file mode 100644 (file)
index 0000000..a38f083
--- /dev/null
@@ -0,0 +1,91 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: arrays continuations db2 io kernel math namespaces
+quotations sequences postgresql.db2.ffi alien alien.c-types
+alien.data db2.types tools.walker ascii splitting math.parser
+combinators libc calendar.format byte-arrays destructors
+prettyprint accessors strings serialize io.encodings.binary
+io.encodings.utf8 alien.strings io.streams.byte-array summary
+present urls specialized-arrays db2.utils db2.connections ;
+SPECIALIZED-ARRAY: uint
+SPECIALIZED-ARRAY: void*
+IN: postgresql.db2.lib
+
+: pq-get-is-null ( handle row column -- ? ) PQgetisnull 1 = ;
+
+: pq-get-string ( handle row column -- obj )
+    3dup PQgetvalue utf8 alien>string
+    dup empty? [ [ pq-get-is-null f ] dip ? ] [ [ 3drop ] dip ] if ;
+
+: pq-get-number ( handle row column -- obj )
+    pq-get-string dup [ string>number ] when ;
+
+: pq-get-boolean ( handle row column -- ? )
+    pq-get-string dup [ "t" = ] when ;
+
+: postgresql-result-error-message ( res -- str/f )
+    dup 0 = [ drop f ] [ PQresultErrorMessage [ blank? ] trim ] if ;
+
+: postgres-result-error ( res -- )
+    postgresql-result-error-message [ throw ] when* ;
+
+: (postgresql-error-message) ( handle -- str )
+    PQerrorMessage
+    "\n" split [ [ blank? ] trim ] map "\n" join ;
+
+: postgresql-error-message ( -- str )
+    db-connection get handle>> (postgresql-error-message) ;
+
+: postgresql-error ( res -- res )
+    dup [ postgresql-error-message throw ] unless ;
+
+ERROR: postgresql-result-null ;
+
+M: postgresql-result-null summary ( obj -- str )
+    drop "PQexec returned f." ;
+
+: postgresql-result-ok? ( res -- ? )
+    [ postgresql-result-null ] unless*
+    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-connection get handle>> swap sql>> PQexec dup postgresql-result-ok? [
+        [ postgresql-result-error-message ] [ PQclear ] bi throw
+    ] unless ;
+
+: default-param-value ( obj -- alien n )
+    ?number>string dup [ utf8 malloc-string &free ] when 0 ;
+
+
+TUPLE: postgresql-malloc-destructor alien ;
+C: <postgresql-malloc-destructor> postgresql-malloc-destructor
+
+M: postgresql-malloc-destructor dispose ( obj -- )
+    alien>> PQfreemem ;
+
+: &postgresql-free ( alien -- alien )
+    dup <postgresql-malloc-destructor> &dispose drop ; inline
+
+: pq-get-blob ( handle row column -- obj/f )
+    [ PQgetvalue ] 3keep 3dup PQgetlength
+    dup 0 > [
+        [ 3drop ] dip
+        [
+            memory>byte-array >string
+            { uint }
+            [
+                PQunescapeBytea dup zero? [
+                    postgresql-result-error-message throw
+                ] [
+                    &postgresql-free
+                ] if
+            ] with-out-parameters memory>byte-array
+        ] with-destructors 
+    ] [
+        drop pq-get-is-null nip [ f ] [ B{ } clone ] if
+    ] if ;
diff --git a/basis/postgresql/db2/queries/authors.txt b/basis/postgresql/db2/queries/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/postgresql/db2/queries/queries.factor b/basis/postgresql/db2/queries/queries.factor
new file mode 100644 (file)
index 0000000..1b8d21d
--- /dev/null
@@ -0,0 +1,30 @@
+! Copyright (C) 2010 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors arrays db2 db2.connections db2.queries
+db2.statements kernel namespaces postgresql.db2
+postgresql.db2.connections.private ;
+IN: postgresql.db2.queries
+
+M: postgresql-db-connection current-db-name
+    db-connection get db>> database>> ;
+
+TUPLE: postgresql-object < sql-object
+    table-catalog
+    table-schema
+    table-name
+    table-type
+    self-referencing-column-name
+    reference-generation
+    user-defined-type-catalog
+    user-defined-type-schema
+    user-defined-type-name
+    is-insertable-into
+    is-typed
+    commit-action ;
+
+TUPLE: postgresql-column < sql-column
+    table_catalog table_schema table_name column_name ordinal_position column_default is_nullable data_type character_maximum_length character_octet_length numeric_precision numeric_precision_radix numeric_scale datetime_precision interval_type interval_precision character_set_catalog character_set_schema character_set_name collation_catalog collation_schema collation_name domain_catalog domain_schema domain_name udt_catalog udt_schema udt_name scope_catalog scope_schema scope_name maximum_cardinality dtd_identifier is_self_referencing is_identity identity_generation identity_start identity_increment identity_maximum identity_minimum identity_cycle is_generated generation_expression is_updatable ;
+
+M: postgresql-db-connection sql-object-class postgresql-object ;
+M: postgresql-db-connection sql-column-class postgresql-column ;
+
diff --git a/basis/postgresql/db2/result-sets/authors.txt b/basis/postgresql/db2/result-sets/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/postgresql/db2/result-sets/result-sets.factor b/basis/postgresql/db2/result-sets/result-sets.factor
new file mode 100644 (file)
index 0000000..e38485e
--- /dev/null
@@ -0,0 +1,135 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors alien.c-types alien.data arrays
+calendar.format combinators db2.binders db2.connections db2.errors
+db2.result-sets db2.statements db2.types db2.utils destructors
+io.encodings.utf8 kernel libc math namespaces
+postgresql.db2.connections postgresql.db2.connections.private
+postgresql.db2.ffi postgresql.db2.lib postgresql.db2.statements
+postgresql.db2.types present sequences serialize
+specialized-arrays urls strings orm.binders ;
+IN: postgresql.db2.result-sets
+SPECIALIZED-ARRAY: uint
+SPECIALIZED-ARRAY: void*
+
+TUPLE: postgresql-result-set < result-set ;
+
+M: postgresql-result-set dispose
+    [ handle>> PQclear ] [ f >>handle drop ] bi ;
+
+M: postgresql-result-set #rows ( result-set -- n )
+    handle>> PQntuples ;
+
+M: postgresql-result-set #columns ( result-set -- n )
+    handle>> PQnfields ;
+
+: result>handle-n ( result-set -- handle n )
+    [ handle>> ] [ n>> ] bi ; inline
+
+M: postgresql-result-set advance-row ( result-set -- )
+    [ 1 + ] change-n drop ;
+
+M: postgresql-result-set more-rows? ( result-set -- ? )
+    [ n>> ] [ max>> ] bi < ;
+
+: type>oid ( symbol -- n )
+    dup array? [ first ] when
+    {
+        { BLOB [ BYTEA-OID ] }
+        { FACTOR-BLOB [ BYTEA-OID ] }
+        [ drop 0 ]
+    } case ;
+
+: type>param-format ( symbol -- n )
+    dup array? [ first ] when
+    {
+        { BLOB [ 1 ] }
+        { FACTOR-BLOB [ 1 ] }
+        [ drop 0 ]
+    } case ;
+
+: param-types ( statement -- seq )
+    in>> [ type>oid ] uint-array{ } map-as ;
+
+: default-param-value ( obj -- alien n )
+    ?number>string dup [ utf8 malloc-string &free ] when 0 ;
+
+ERROR: postgresql-obj-error obj ;
+: obj>value/type ( obj -- value type )
+    {
+        { [ dup string? ] [ VARCHAR ] }
+        { [ dup array? ] [ first2 ] }
+        { [ dup in-binder-low? ] [ [ value>> ] [ type>> ] bi ] }
+        { [ dup column-binder-in? ] [ [ value>> ] [ column>> type>> ] bi ] }
+        [ postgresql-obj-error ]
+    } cond ;
+
+: param-values ( statement -- seq seq2 )
+    in>>
+    [
+        obj>value/type
+        {
+            { FACTOR-BLOB [
+                dup [ object>bytes malloc-byte-array/length ] [ 0 ] if
+            ] }
+            { BLOB [ dup [ malloc-byte-array/length ] [ 0 ] if ] }
+            { DATE [ dup [ timestamp>ymd ] when default-param-value ] }
+            { TIME [ dup [ timestamp>hms ] when default-param-value ] }
+            { DATETIME [ dup [ timestamp>ymdhms ] when default-param-value ] }
+            { TIMESTAMP [ dup [ timestamp>ymdhms ] when default-param-value ] }
+            { URL [ dup [ present ] when default-param-value ] }
+            [ drop default-param-value ]
+        } case 2array
+    ] map flip [
+        f f
+    ] [
+        first2 [ void*-array{ } like ] [ uint-array{ } like ] bi*
+    ] if-empty ;
+
+: param-formats ( statement -- seq )
+    in>> [ type>param-format ] uint-array{ } map-as ;
+
+M: postgresql-db-connection statement>result-set ( statement -- result-set )
+    dup
+    [
+        [ db-connection get handle>> ] dip
+        {
+            [ sql>> ]
+            [ in>> length ]
+            [ param-types ]
+            [ param-values ]
+            [ param-formats ]
+        } cleave
+        0 PQexecParams dup postgresql-result-ok? [
+            [
+                postgresql-result-error-message parse-sql-error
+            ] [ PQclear ] bi throw
+        ] unless
+    ] with-destructors
+    \ postgresql-result-set new-result-set
+    init-result-set ;
+
+M: postgresql-result-set column
+    [ [ handle>> ] [ n>> ] bi ] 2dip
+    dup array? [ first ] when
+    {
+        { +db-assigned-key+ [ pq-get-number ] }
+        { +random-key+ [ pq-get-number ] }
+        { INTEGER [ pq-get-number ] }
+        { BIG-INTEGER [ pq-get-number ] }
+        { DOUBLE [ pq-get-number ] }
+        { TEXT [ pq-get-string ] }
+        { VARCHAR [ pq-get-string ] }
+        { CHARACTER [ pq-get-string ] }
+        { DATE [ pq-get-string dup [ ymd>timestamp ] when ] }
+        { TIME [ pq-get-string dup [ hms>duration ] when ] }
+        { TIMESTAMP [ pq-get-string dup [ ymdhms>timestamp ] when ] }
+        { DATETIME [ pq-get-string dup [ ymdhms>timestamp ] when ] }
+        { BLOB [ pq-get-blob ] }
+        { BOOLEAN [ pq-get-boolean ] }
+        { URL [ pq-get-string dup [ >url ] when ] }
+        { FACTOR-BLOB [
+            pq-get-blob
+            dup [ bytes>object ] when ] }
+        [ no-sql-type ]
+    } case ;
diff --git a/basis/postgresql/db2/statements/authors.txt b/basis/postgresql/db2/statements/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/postgresql/db2/statements/statements.factor b/basis/postgresql/db2/statements/statements.factor
new file mode 100644 (file)
index 0000000..15969a7
--- /dev/null
@@ -0,0 +1,28 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors db2.connections
+postgresql.db2.connections.private postgresql.db2.ffi
+postgresql.db2.lib db2.statements destructors kernel namespaces
+sequences math.parser ;
+IN: postgresql.db2.statements
+
+M: postgresql-db-connection prepare-statement*
+    dup
+    [ db-connection get handle>> "statementname-can'tbef?" ] dip
+    [ sql>> ] [ in>> ] bi length f
+    PQprepare postgresql-error >>handle ;
+
+M: postgresql-db-connection dispose-statement
+    dup handle>> PQclear
+    f >>handle drop ;
+
+M: postgresql-db-connection bind-sequence drop ;
+
+SYMBOL: postgresql-bind-counter
+
+M: postgresql-db-connection init-bind-index ( -- )
+    1 postgresql-bind-counter set ;
+
+M: postgresql-db-connection next-bind-index ( -- string )
+    postgresql-bind-counter
+    [ get number>string ] [ inc ] bi "$" prepend ;
diff --git a/basis/postgresql/db2/tests/authors.txt b/basis/postgresql/db2/tests/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/postgresql/db2/tests/tests.factor b/basis/postgresql/db2/tests/tests.factor
new file mode 100644 (file)
index 0000000..95234b0
--- /dev/null
@@ -0,0 +1,24 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors db2 db2.statements db2.statements.tests db2.debug
+tools.test ;
+IN: postgresql.db2.tests
+
+: test-sql-bound-commands ( -- )
+    create-computer-table
+    
+    [ ] [
+        <statement>
+            "insert into computer (name, os, version) values($1, $2, $3);" >>sql
+            { "clubber" "windows" "7" } >>in
+        sql-command
+    ] unit-test
+
+    [ { { "windows" } } ] [
+        <statement>
+            "select os from computer where name = $1;" >>sql
+            { "clubber" } >>in
+        sql-query
+    ] unit-test ;
+
+[ test-sql-bound-commands ] test-postgresql
diff --git a/basis/postgresql/db2/types/authors.txt b/basis/postgresql/db2/types/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/postgresql/db2/types/types.factor b/basis/postgresql/db2/types/types.factor
new file mode 100644 (file)
index 0000000..abe44fe
--- /dev/null
@@ -0,0 +1,97 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors alien.c-types alien.strings arrays
+calendar.format combinators postgresql.db2.connections
+postgresql.db2.connections.private
+postgresql.db2.ffi postgresql.db2.lib db2.types destructors
+io.encodings.utf8 kernel math math.parser multiline sequences
+serialize strings urls ;
+IN: postgresql.db2.types
+
+M: postgresql-db-connection sql-type>string
+    dup array? [ first ] when
+    {
+        { INTEGER [ "INTEGER" ] }
+        { BIG-INTEGER [ "INTEGER " ] }
+        { SIGNED-BIG-INTEGER [ "BIGINT" ] }
+        { UNSIGNED-BIG-INTEGER [ "BIGINT" ] }
+        { DOUBLE [ "DOUBLE" ] }
+        { REAL [ "DOUBLE" ] }
+        { BOOLEAN [ "BOOLEAN" ] }
+        { TEXT [ "TEXT" ] }
+        { VARCHAR [ "TEXT" ] }
+        { CHARACTER [ "TEXT" ] }
+        { DATE [ "DATE" ] }
+        { TIME [ "TIME" ] }
+        { DATETIME [ "TIMESTAMP" ] }
+        { TIMESTAMP [ "TIMESTAMP" ] }
+        { BLOB [ "BYTEA" ] }
+        { FACTOR-BLOB [ "BYTEA" ] }
+        { URL [ "TEXT" ] }
+        { +db-assigned-key+ [ "INTEGER" ] }
+        { +random-key+ [ "BIGINT" ] }
+        [ no-sql-type ]
+    } case ;
+
+M: postgresql-db-connection sql-create-type>string
+    dup array? [ first ] when
+    {
+        { INTEGER [ "INTEGER" ] }
+        { BIG-INTEGER [ "INTEGER " ] }
+        { SIGNED-BIG-INTEGER [ "BIGINT" ] }
+        { UNSIGNED-BIG-INTEGER [ "BIGINT" ] }
+        { DOUBLE [ "DOUBLE" ] }
+        { REAL [ "DOUBLE" ] }
+        { BOOLEAN [ "BOOLEAN" ] }
+        { TEXT [ "TEXT" ] }
+        { VARCHAR [ "TEXT" ] }
+        { CHARACTER [ "TEXT" ] }
+        { DATE [ "DATE" ] }
+        { TIME [ "TIME" ] }
+        { DATETIME [ "TIMESTAMP" ] }
+        { TIMESTAMP [ "TIMESTAMP" ] }
+        { BLOB [ "BYTEA" ] }
+        { FACTOR-BLOB [ "BYTEA" ] }
+        { URL [ "TEXT" ] }
+        { +db-assigned-key+ [ "SERIAL" ] }
+        { +random-key+ [ "BIGINT" ] }
+        [ no-sql-type ]
+    } case ;
+
+/*
+: postgresql-column-typed ( handle row column type -- obj )
+    dup array? [ first ] when
+    {
+        { +db-assigned-key+ [ pq-get-number ] }
+        { +random-key+ [ pq-get-number ] }
+        { INTEGER [ pq-get-number ] }
+        { BIG-INTEGER [ pq-get-number ] }
+        { DOUBLE [ pq-get-number ] }
+        { TEXT [ pq-get-string ] }
+        { VARCHAR [ pq-get-string ] }
+        { CHARACTER [ pq-get-string ] }
+        { DATE [ pq-get-string dup [ ymd>timestamp ] when ] }
+        { TIME [ pq-get-string dup [ hms>duration ] when ] }
+        { TIMESTAMP [ pq-get-string dup [ ymdhms>timestamp ] when ] }
+        { DATETIME [ pq-get-string dup [ ymdhms>timestamp ] when ] }
+        { BLOB [ pq-get-blob ] }
+        { URL [ pq-get-string dup [ >url ] when ] }
+        { FACTOR-BLOB [
+            pq-get-blob
+            dup [ bytes>object ] when ] }
+        [ no-sql-type ]
+    } case ;
+*/
+
+: postgresql-modifier>string ( symbol -- string )
+    {
+        { NULL [ "NULL" ] }
+        { NOT-NULL [ "NOT NULL" ] }
+        { SERIAL [ "SERIAL" ] }
+        { AUTOINCREMENT [ "AUTOINCREMENT" ] }
+        { +primary-key+ [ "" ] }
+        [ no-sql-modifier ]
+    } case ;
+
+M: postgresql-db-connection sql-modifiers>string
+    [ postgresql-modifier>string ] map " " join ;
diff --git a/basis/postgresql/orm/authors.txt b/basis/postgresql/orm/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/postgresql/orm/orm.factor b/basis/postgresql/orm/orm.factor
new file mode 100644 (file)
index 0000000..e9a77c7
--- /dev/null
@@ -0,0 +1,8 @@
+! Copyright (C) 2010 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: sequences vocabs.loader vocabs ;
+IN: postgresql.orm
+
+{
+    "postgresql.orm.queries"
+} [ require ] each
diff --git a/basis/postgresql/orm/queries/authors.txt b/basis/postgresql/orm/queries/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/postgresql/orm/queries/queries.factor b/basis/postgresql/orm/queries/queries.factor
new file mode 100644 (file)
index 0000000..2a681eb
--- /dev/null
@@ -0,0 +1,169 @@
+! Copyright (C) 2010 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors arrays assocs combinators combinators.smart db2
+db2.binders db2.statements db2.types db2.utils kernel make math
+math.intervals math.parser multiline namespaces orm.persistent
+orm.queries postgresql.db2.connections.private ranges sequences ;
+IN: postgresql.orm.queries
+
+! TODOOOOOO
+SYMBOL: postgresql-counter
+
+: next-bind ( -- string )
+    postgresql-counter [ inc ] [ get ] bi
+    number>string "$" prepend ;
+
+M: postgresql-db-connection n>bind-sequence ( n -- sequence )
+    [1..b] [ number>string "$" prepend ] map ;
+
+M:: postgresql-db-connection continue-bind-sequence ( previous n -- sequence )
+    previous 1 +
+    dup n +
+    [a..b] [ number>string "$" prepend ] map ;
+
+ERROR: db-assigned-keys-not-empty assoc ;
+: check-db-assigned-assoc ( assoc -- assoc )
+    dup [ first column-primary-key? ] filter
+    [ db-assigned-keys-not-empty ] unless-empty ;
+
+M: postgresql-db-connection insert-db-assigned-key-sql
+    [ <statement> ] dip
+    [ >persistent ] [ ] bi {
+        [ drop [ "select " "add_" ] dip table-name>> trim-double-quotes 3append quote-sql-name add-sql "(" add-sql ]
+        [
+
+            filter-tuple-values check-db-assigned-assoc
+            [ length n>bind-string add-sql ");" add-sql ]
+            [ [ [ second ] [ first type>> ] bi <in-binder-low> ] map >>in ] bi
+            { INTEGER } >>out
+        ]
+    } 2cleave ;
+
+M: postgresql-db-connection insert-tuple-set-key ( tuple statement -- )
+    sql-query first first set-primary-key drop ;
+
+M: postgresql-db-connection insert-user-assigned-key-sql
+    [ <statement> ] dip
+    [ >persistent ] [ ] bi {
+        [ drop table-name>> quote-sql-name "INSERT INTO " "(" surround add-sql ]
+        [
+            filter-tuple-values
+            [
+                keys
+                [ [ column-name>> quote-sql-name ] map ", " join ]
+                [
+                    length n>bind-string
+                    ") values(" ");" surround
+                ] bi append add-sql
+            ]
+            [ [ [ second ] [ first type>> ] bi <in-binder-low> ] map >>in ] bi
+        ]
+    } 2cleave ;
+
+/*
+M: postgresql-db-connection insert-user-assigned-key-sql
+    [ <statement> ] dip >persistent {
+        [ table-name>> quote-sql-name "INSERT INTO " prepend add-sql "(" add-sql ]
+        [
+            [
+                columns>>
+                [
+                    [
+                        [ ", " % ] [ column-name>> quote-sql-name % ] interleave 
+                        ")" %
+                    ] "" make add-sql
+                ] [
+                    " values(" %
+                    [ ", " % ] [
+                        dup type>> +random-key+ = [
+                            [
+                                bind-name%
+                                slot-name>>
+                                f
+                                random-id-generator
+                            ] [ type>> ] bi <generator-bind> 1,
+                        ] [
+                            bind%
+                        ] if
+                    ] interleave
+                    ");" 0%
+                ] bi
+            ]
+    } cleave ;
+*/
+
+
+: postgresql-create-table ( tuple-class -- string )
+    >persistent dup table-name>> quote-sql-name
+    [
+        [
+            [ columns>> ] dip
+            "CREATE TABLE " % %
+            "(" % [ ", " % ] [
+                [ column-name>> quote-sql-name % " " % ]
+                [ type>> sql-create-type>string % ]
+                [ drop ] tri
+                ! [ modifiers % ] bi
+            ] interleave
+        ] [
+            drop
+            find-primary-key [
+                ", " %
+                "PRIMARY KEY(" %
+                [ "," % ] [ column-name>> quote-sql-name % ] interleave
+                ")" %
+            ] unless-empty
+            ");" %
+        ] 2bi
+    ] "" make ;
+
+:: postgresql-create-function ( tuple-class -- string )
+    tuple-class >persistent :> persistent
+    persistent table-name>> :> table-name
+    table-name trim-double-quotes :> table-name-unquoted
+    persistent columns>> :> columns
+    columns remove-primary-key :> columns-minus-key
+
+    [
+        "CREATE FUNCTION " "add_" table-name-unquoted append quote-sql-name "("
+
+        columns-minus-key [ type>> sql-type>string ] map ", " join
+
+        ") returns bigint as 'insert into "
+
+        table-name quote-sql-name "(" columns-minus-key [ column-name>> quote-sql-name ] map ", " join
+        ") values("
+        1 columns-minus-key length [a,b]
+        [ number>string "$" prepend ] map ", " join
+
+        "); select currval(''" table-name-unquoted "_"
+        persistent find-primary-key first column-name>>
+        "_seq'');' language sql;"
+    ] "" append-outputs-as ;
+
+M: postgresql-db-connection create-table-sql ( tuple-class -- seq )
+    [ postgresql-create-table ]
+    [ dup db-assigned-key? [ postgresql-create-function 2array ] [ drop ] if ] bi ;
+
+:: postgresql-drop-table ( tuple-class -- string )
+    tuple-class >persistent table-name>> :> table-name
+    [
+        "drop table " table-name quote-sql-name ";"
+    ] "" append-outputs-as ;
+
+:: postgresql-drop-function ( tuple-class -- string )
+    tuple-class >persistent :> persistent
+    persistent table-name>> :> table-name
+    table-name trim-double-quotes :> table-name-unquoted
+    persistent columns>> :> columns
+    columns remove-primary-key :> columns-minus-key
+    [
+        "drop function " "add_" table-name-unquoted append quote-sql-name
+        "("
+        columns-minus-key [ type>> sql-type>string ] map ", " join
+        ");"
+    ] "" append-outputs-as ;
+
+M: postgresql-db-connection drop-table-sql ( tuple-class -- seq )
+    [ postgresql-drop-table ]
+    [ dup db-assigned-key? [ postgresql-drop-function 2array ] [ drop ] if ] bi ;
diff --git a/basis/postgresql/postgresql.factor b/basis/postgresql/postgresql.factor
new file mode 100644 (file)
index 0000000..59015e1
--- /dev/null
@@ -0,0 +1,9 @@
+! Copyright (C) 2010 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: sequences vocabs vocabs.loader ;
+IN: postgresql
+
+{
+    "postgresql.db2"
+    "postgresql.orm"
+} [ require ] each
diff --git a/basis/reconstructors/authors.txt b/basis/reconstructors/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/reconstructors/reconstructors.factor b/basis/reconstructors/reconstructors.factor
new file mode 100644 (file)
index 0000000..84e0f37
--- /dev/null
@@ -0,0 +1,205 @@
+! Copyright (C) 2010 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors assocs fry grouping hashtables kernel make
+math multiline sequences splitting.monotonic ;
+IN: reconstructors
+
+
+TUPLE: reconstructor row-spec combiner-spec ;
+
+
+
+/*
+ERROR: no-setter ;
+
+: out-binder>setter ( toc -- word )
+    [ class>> >persistent columns>> ]
+    [ toc>> column-name>> ] bi '[ column-name>> _ = ] find
+    nip [ no-setter ] unless* setter>> ;
+
+MACRO: query-object>reconstructor ( tuple -- quot )
+    out>> [ [ class>> ] bi@ = ] monotonic-split
+    [ [ first class>> ] [ [ out-binder>setter ] map ] bi ] { } map>assoc
+    [
+        [
+            first2
+            [ , \ new , ]
+            [ reverse [ \ swap , , (( obj obj -- obj )) , \ call-effect , ] each ] bi*
+        ] each
+    ] [ ] make '[ [ _ input<sequence ] ] ;
+*/
+
+
+/*
+
+
+
+TUPLE: bag id beans ;
+TUPLE: bean id bag-id color ;
+
+{
+    { 0 0 0 "blue" } { 0 1 0 "red" } { 0 2 0 "yellow" }
+    { 1 3 1 "black" } { 1 4 1 "white" } 
+    { 2 5 2 "black" } { 2 6 2 "white" }
+}
+{ { bag >>id } { bean >>id >>bag-id >>color } } rows>tuples
+
+[ [ second bag-id>> ] bi@ = ] monotonic-split
+[ [ first first ] [ [ second ] map ] bi >>beans ] map
+
+
+
+T{ reconstructor
+    { row-spec
+        { { bag >>id } { bean >>id >>color } }
+    }
+    { combiner-spec
+        { bag >>beans { bean bag-id>> } }
+    }
+}
+
+TUPLE: foo-1 a b ;
+
+{ { 1 "Asdf" } }
+{ { foo-1 >>a >>b } }
+
+T{ foo-1 { a 1 } { b "Asdf" } }
+
+
+{
+    { big-container >>id >>a }
+    { medium-container >>id >>big-container-id >>b }
+    { small-container >>id >>medium-container-id >>c }
+} rows>tuples
+
+
+{
+    { third medium-container-id>> <=> }
+    { second big-container-id>> <=> }
+} sort-by
+
+
+
+T{ reconstructor
+    { row-spec
+        {
+            { big-container >>id >>a }
+            { medium-container >>id >>big-container-id >>b }
+            { small-container >>id >>medium-container-id >>c }
+        }
+    }
+    { combiner-spec
+        {
+            T{ nested-reconstructor f
+                small-container medium-container-id>>
+                medium-container >>small-containers
+            }
+
+            T{ nested-reconstructor f
+                medium-container big-container-id>>
+                big-container >>medium-containers
+            }
+        }
+    }
+}
+
+
+TUPLE: big-container id a medium-containers ;
+
+TUPLE: medium-container id big-container-id b small-containers ;
+
+TUPLE: small-container id medium-container-id c ;
+
+
+TUPLE: nested-reconstructor
+    from-class from-accessor
+    to-class to-accessor ;
+
+
+{
+    { 0 "a" 0 0 "b" 0 0 "c" }
+    { 0 "a" 0 0 "b" 1 0 "c" }
+    { 0 "a" 1 0 "b" 2 1 "c" }
+    { 1 "a" 2 1 "b" 3 2 "c" }
+    { 1 "a" 2 1 "b" 4 2 "c" }
+    { 1 "a" 2 1 "b" 5 2 "c" }
+    { 1 "a" 3 1 "b" 6 3 "c" }
+    { 2 "a" 4 2 "b" 7 4 "c" }
+    { 2 "a" 4 2 "b" 8 4 "c" }
+    { 2 "a" 5 2 "b" 9 5 "c" }
+    { 2 "a" 5 2 "b" 10 5 "c" }
+    { 2 "a" 5 2 "b" 11 5 "c" }
+    { 2 "a" 6 2 "b" 12 6 "c" }
+}
+
+{
+    { big-container >>id >>a }
+    { medium-container >>id >>big-container-id >>b }
+    { small-container >>id >>medium-container-id >>c }
+} rows>tuples
+
+
+[ [ 2 swap nth medium-container-id>> ] bi@ = ] monotonic-split
+[
+    [ [ third ] map ] [ first second small-containers<< ] [ ] tri
+    first but-last
+] map
+
+[ [ 1 swap nth big-container-id>> ] bi@ = ] monotonic-split
+[
+    [ [ second ] map ] [ first first medium-containers<< ] [ ] tri
+    first but-last
+] map
+
+[ first ] map
+
+{
+    T{ nested-reconstructor f
+        small-container medium-container-id>>
+        medium-container >>small-containers
+    }
+
+    T{ nested-reconstructor f
+        medium-container big-container-id>>
+        big-container >>medium-containers
+    }
+}
+
+
+
+
+
+*/
+
+ERROR: not-found key ;
+
+: at- ( class hashtable -- n )
+    ?at [ not-found ] unless ;
+
+: nth-tuple, ( n hashtable -- )
+    at- , \ swap , \ nth , ;
+
+: splitter-quot ( combiner-spec lookup-table -- quot )
+    '[
+        _ [ _ nth-tuple, ] each
+    ] [ ] make
+    '[ [ _ bi@ = ] monotonic-split ] ;
+
+: reconstructor>tuple-lookup-table ( reconstructor -- hashtable )
+    row-spec>> [ [ first ] map ] [ length <iota> ] bi zip >hashtable ; 
+
+: split-by-length ( seq lengths -- seq' )
+    0 [ + ] accumulate swap suffix 2 <clumps>
+    [ first2 rot subseq ] with map ;
+
+: fill-new-tuple ( seq spec -- tuple )
+    unclip new [
+        '[ [ _ ] 2dip execute( a obj -- obj ) drop ] 2each
+    ] keep ;
+
+: row>tuples ( seq spec -- seq' )
+    [ [ length 1 - ] map split-by-length ] keep
+    [ fill-new-tuple ] 2map ;
+
+: rows>tuples ( seq spec -- seq' )
+    '[ _ row>tuples ] map ;
diff --git a/basis/sqlite/authors.txt b/basis/sqlite/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/sqlite/db2/authors.txt b/basis/sqlite/db2/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/sqlite/db2/connections/authors.txt b/basis/sqlite/db2/connections/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/sqlite/db2/connections/connections-tests.factor b/basis/sqlite/db2/connections/connections-tests.factor
new file mode 100644 (file)
index 0000000..87b3e21
--- /dev/null
@@ -0,0 +1,8 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: sqlite.db2 db2.debug tools.test ;
+IN: sqlite.db2.connections.tests
+
+[ ] [
+    [ ] test-sqlite
+] unit-test
diff --git a/basis/sqlite/db2/connections/connections.factor b/basis/sqlite/db2/connections/connections.factor
new file mode 100644 (file)
index 0000000..872f4b7
--- /dev/null
@@ -0,0 +1,23 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors combinators db2.connections sqlite.db2
+sqlite.db2.errors sqlite.db2.lib kernel db2.errors io.backend
+destructors ;
+IN: sqlite.db2.connections
+
+TUPLE: sqlite-db-connection < db-connection ;
+
+: <sqlite-db-connection> ( handle -- db-connection )
+    sqlite-db-connection new-db-connection ;
+
+M: sqlite-db db>db-connection-generic ( db -- db-connection )
+    path>> normalize-path sqlite-open <sqlite-db-connection> ;
+
+M: sqlite-db-connection dispose* ( db-connection -- )
+    [ handle>> sqlite-close ] [ f >>handle drop ] bi ;
+
+M: sqlite-db-connection parse-sql-error ( error -- error' )
+    dup n>> {
+        { 1 [ string>> parse-sqlite-sql-error ] }
+        [ drop ]
+    } case ;
diff --git a/basis/sqlite/db2/db2.factor b/basis/sqlite/db2/db2.factor
new file mode 100644 (file)
index 0000000..84c97cb
--- /dev/null
@@ -0,0 +1,23 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: constructors db2.connections kernel sequences vocabs
+vocabs.loader ;
+IN: sqlite.db2
+
+TUPLE: sqlite-db path ;
+
+CONSTRUCTOR: <sqlite-db> sqlite-db ( path -- db ) ;
+
+{
+    "sqlite.db2.connections"
+    "sqlite.db2.errors"
+    "sqlite.db2.ffi"
+    "sqlite.db2.lib"
+    "sqlite.db2.result-sets"
+    "sqlite.db2.statements"
+    "sqlite.db2.types"
+    "sqlite.db2.queries"
+    ! "sqlite.db2.introspection"
+
+    "sqlite.orm"
+} [ require ] each
diff --git a/basis/sqlite/db2/errors/authors.txt b/basis/sqlite/db2/errors/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/sqlite/db2/errors/errors.factor b/basis/sqlite/db2/errors/errors.factor
new file mode 100644 (file)
index 0000000..eead49e
--- /dev/null
@@ -0,0 +1,35 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors combinators db2.connections db2.errors
+sqlite.db2.ffi kernel locals namespaces peg.ebnf sequences
+strings ;
+IN: sqlite.db2.errors
+
+ERROR: sqlite-error < db-error n string ;
+ERROR: sqlite-sql-error < sql-error n string ;
+
+: sqlite-statement-error ( -- * )
+    SQLITE_ERROR
+    db-connection get handle>> sqlite3_errmsg sqlite-sql-error ;
+
+TUPLE: unparsed-sqlite-error error ;
+C: <unparsed-sqlite-error> unparsed-sqlite-error
+
+EBNF: parse-sqlite-sql-error [=[
+
+TableMessage = " already exists"
+SyntaxError = ": syntax error"
+
+SqliteError =
+    "table " (!(TableMessage).)+:table TableMessage:message
+      => [[ table >string <sql-table-exists> ]]
+    | "near " (!(SyntaxError).)+:syntax SyntaxError:message
+      => [[ syntax >string <sql-syntax-error> ]]
+    | "no such table: " .+:table
+      => [[ table >string <sql-table-missing> ]]
+    | .*:error
+      => [[ error >string <unparsed-sqlite-error> ]]
+]=]
+
+: throw-sqlite-error ( n -- * )
+    dup sqlite-error-messages nth sqlite-error ;
diff --git a/basis/sqlite/db2/ffi/ffi.factor b/basis/sqlite/db2/ffi/ffi.factor
new file mode 100644 (file)
index 0000000..4b1eda1
--- /dev/null
@@ -0,0 +1,142 @@
+! Copyright (C) 2005 Chris Double, Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+! Not all functions have been wrapped.
+USING: alien alien.c-types alien.libraries alien.syntax
+combinators system ;
+IN: sqlite.db2.ffi
+
+<< "sqlite" {
+        { [ os windows? ]  [ "sqlite3.dll" ] }
+        { [ os macosx? ] [ "/usr/lib/libsqlite3.dylib" ] }
+        { [ os unix? ]  [ "libsqlite3.so" ] }
+    } cond cdecl add-library >>
+
+LIBRARY: sqlite
+
+! Return values from sqlite functions
+CONSTANT: SQLITE_OK           0 ! Successful result
+CONSTANT: SQLITE_ERROR        1 ! SQL error or missing database
+CONSTANT: SQLITE_INTERNAL     2 ! An internal logic error in SQLite
+CONSTANT: SQLITE_PERM         3 ! Access permission denied
+CONSTANT: SQLITE_ABORT        4 ! Callback routine requested an abort
+CONSTANT: SQLITE_BUSY         5 ! The database file is locked
+CONSTANT: SQLITE_LOCKED       6 ! A table in the database is locked
+CONSTANT: SQLITE_NOMEM        7 ! A malloc() failed
+CONSTANT: SQLITE_READONLY     8 ! Attempt to write a readonly database
+CONSTANT: SQLITE_INTERRUPT    9 ! Operation terminated by sqlite_interrupt()
+CONSTANT: SQLITE_IOERR       10 ! Some kind of disk I/O error occurred
+CONSTANT: SQLITE_CORRUPT     11 ! The database disk image is malformed
+CONSTANT: SQLITE_NOTFOUND    12 ! (Internal Only) Table or record not found
+CONSTANT: SQLITE_FULL        13 ! Insertion failed because database is full
+CONSTANT: SQLITE_CANTOPEN    14 ! Unable to open the database file
+CONSTANT: SQLITE_PROTOCOL    15 ! Database lock protocol error
+CONSTANT: SQLITE_EMPTY       16 ! (Internal Only) Database table is empty
+CONSTANT: SQLITE_SCHEMA      17 ! The database schema changed
+CONSTANT: SQLITE_TOOBIG      18 ! Too much data for one row of a table
+CONSTANT: SQLITE_CONSTRAINT  19 ! Abort due to contraint violation
+CONSTANT: SQLITE_MISMATCH    20 ! Data type mismatch
+CONSTANT: SQLITE_MISUSE      21 ! Library used incorrectly
+CONSTANT: SQLITE_NOLFS       22 ! Uses OS features not supported on host
+CONSTANT: SQLITE_AUTH        23 ! Authorization denied
+CONSTANT: SQLITE_FORMAT      24 ! Auxiliary database format error
+CONSTANT: SQLITE_RANGE       25 ! 2nd parameter to sqlite3_bind out of range
+CONSTANT: SQLITE_NOTADB      26 ! File opened that is not a database file
+
+CONSTANT: sqlite-error-messages
+{
+    "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"
+}
+
+! Return values from sqlite3_step
+CONSTANT: SQLITE_ROW         100
+CONSTANT: SQLITE_DONE        101
+
+! Return values from the sqlite3_column_type function
+CONSTANT: SQLITE_INTEGER     1
+CONSTANT: SQLITE_FLOAT       2
+CONSTANT: SQLITE_TEXT        3
+CONSTANT: SQLITE_BLOB        4
+CONSTANT: SQLITE_NULL        5
+
+! Values for the 'destructor' parameter of the 'bind' routines.
+CONSTANT: SQLITE_STATIC      0
+CONSTANT: SQLITE_TRANSIENT   -1
+
+CONSTANT: SQLITE_OPEN_READONLY         0x00000001
+CONSTANT: SQLITE_OPEN_READWRITE        0x00000002
+CONSTANT: SQLITE_OPEN_CREATE           0x00000004
+CONSTANT: SQLITE_OPEN_DELETEONCLOSE    0x00000008
+CONSTANT: SQLITE_OPEN_EXCLUSIVE        0x00000010
+CONSTANT: SQLITE_OPEN_MAIN_DB          0x00000100
+CONSTANT: SQLITE_OPEN_TEMP_DB          0x00000200
+CONSTANT: SQLITE_OPEN_TRANSIENT_DB     0x00000400
+CONSTANT: SQLITE_OPEN_MAIN_JOURNAL     0x00000800
+CONSTANT: SQLITE_OPEN_TEMP_JOURNAL     0x00001000
+CONSTANT: SQLITE_OPEN_SUBJOURNAL       0x00002000
+CONSTANT: SQLITE_OPEN_MASTER_JOURNAL   0x00004000
+
+C-TYPE: sqlite3
+C-TYPE: sqlite3_stmt
+TYPEDEF: longlong sqlite3_int64
+TYPEDEF: ulonglong sqlite3_uint64
+
+LIBRARY: sqlite
+FUNCTION: int sqlite3_open ( c-string filename, void* ppDb )
+FUNCTION: int sqlite3_close ( sqlite3* pDb )
+FUNCTION: c-string sqlite3_errmsg ( sqlite3* pDb )
+FUNCTION: int sqlite3_prepare ( sqlite3* pDb, c-string zSql, int nBytes, void* ppStmt, void* pzTail )
+FUNCTION: int sqlite3_prepare_v2 ( sqlite3* pDb, c-string 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: sqlite3_uint64 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_double ( sqlite3_stmt* pStmt, int index, double x )
+FUNCTION: int sqlite3_bind_int ( sqlite3_stmt* pStmt, int index, int n )
+FUNCTION: int sqlite3_bind_int64 ( sqlite3_stmt* pStmt, int index, sqlite3_int64 n )
+! Bind the same function as above, but for unsigned 64bit integers
+FUNCTION-ALIAS: sqlite3-bind-uint64
+    int sqlite3_bind_int64 ( sqlite3_stmt* pStmt, int index, sqlite3_uint64 in64 )
+FUNCTION: int sqlite3_bind_null ( sqlite3_stmt* pStmt, int n )
+FUNCTION: int sqlite3_bind_text ( sqlite3_stmt* pStmt, int index, c-string text, int len, int destructor )
+FUNCTION: int sqlite3_bind_parameter_index ( sqlite3_stmt* pStmt, c-string name )
+FUNCTION: int sqlite3_clear_bindings ( sqlite3_stmt* pStmt )
+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: c-string sqlite3_column_decltype ( sqlite3_stmt* pStmt, int col )
+FUNCTION: int sqlite3_column_int ( sqlite3_stmt* pStmt, int col )
+FUNCTION: sqlite3_int64 sqlite3_column_int64 ( sqlite3_stmt* pStmt, int col )
+! Bind the same function as above, but for unsigned 64bit integers
+FUNCTION-ALIAS: sqlite3-column-uint64
+    sqlite3_uint64 sqlite3_column_int64 ( sqlite3_stmt* pStmt, int col )
+FUNCTION: double sqlite3_column_double ( sqlite3_stmt* pStmt, int col )
+FUNCTION: c-string sqlite3_column_name ( sqlite3_stmt* pStmt, int col )
+FUNCTION: c-string sqlite3_column_text ( sqlite3_stmt* pStmt, int col )
+FUNCTION: int sqlite3_column_type ( sqlite3_stmt* pStmt, int col )
diff --git a/basis/sqlite/db2/introspection/authors.txt b/basis/sqlite/db2/introspection/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/sqlite/db2/introspection/introspection.factor b/basis/sqlite/db2/introspection/introspection.factor
new file mode 100644 (file)
index 0000000..973f61d
--- /dev/null
@@ -0,0 +1,30 @@
+! Copyright (C) 2011 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: db2.introspection db2.types kernel orm.persistent
+orm.tuples sqlite.db2.connections accessors sequences ;
+IN: sqlite.db2.introspection
+
+TUPLE: sqlite-object type name tbl-name rootpage sql ;
+TUPLE: temporary-sqlite-object < sqlite-object ;
+
+PERSISTENT: { sqlite-object "sqlite_master" }
+    { "type" TEXT }
+    { "name" TEXT }
+    { "tbl-name" TEXT }
+    { "rootpage" INTEGER }
+    { "sql" TEXT } ;
+
+PERSISTENT: { temporary-sqlite-object "sqlite_temp_master" } ;
+
+M: sqlite-db-connection all-db-objects
+    sqlite-object new select-tuples ;
+
+M: sqlite-db-connection all-tables
+    all-db-objects [ type>> "table" = ] filter ;
+
+M: sqlite-db-connection all-indices
+    all-db-objects [ type>> "index" = ] filter ;
+
+M: sqlite-db-connection temporary-db-objects
+    temporary-sqlite-object new select-tuples ;
+
diff --git a/basis/sqlite/db2/lib/lib.factor b/basis/sqlite/db2/lib/lib.factor
new file mode 100644 (file)
index 0000000..fb8a45e
--- /dev/null
@@ -0,0 +1,119 @@
+! Copyright (C) 2008 Chris Double, Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors alien.c-types alien.data arrays assocs
+byte-arrays calendar.format combinators continuations db2
+db2.connections db2.errors sqlite.db2.errors sqlite.db2.ffi
+db2.types io io.backend io.encodings.binary io.encodings.string
+io.encodings.utf8 io.streams.byte-array kernel math math.parser
+namespaces present sequences serialize shuffle urls ;
+IN: sqlite.db2.lib
+
+: sqlite-check-result ( n -- )
+    {
+        { SQLITE_OK [ ] }
+        { SQLITE_ERROR [ sqlite-statement-error ] }
+        [ throw-sqlite-error ]
+    } case ;
+
+: sqlite-open ( path -- db )
+    normalize-path
+    { void* } [ sqlite3_open sqlite-check-result ]
+    with-out-parameters ;
+
+: sqlite-close ( db -- )
+    sqlite3_close sqlite-check-result ;
+
+: sqlite-prepare ( db sql -- handle )
+    utf8 encode dup length
+    { void* void* }
+    [ sqlite3_prepare_v2 sqlite-check-result ]
+    with-out-parameters drop ;
+
+: sqlite-bind-parameter-index ( handle name -- index )
+    sqlite3_bind_parameter_index ;
+
+: parameter-index ( handle name text -- handle name text )
+    [ dupd sqlite-bind-parameter-index ] dip ;
+
+: sqlite-bind-text ( handle index text -- )
+    utf8 encode dup length SQLITE_TRANSIENT
+    sqlite3_bind_text sqlite-check-result ;
+
+: sqlite-bind-int ( handle i n -- )
+    sqlite3_bind_int sqlite-check-result ;
+
+: sqlite-bind-int64 ( handle i n -- )
+    sqlite3_bind_int64 sqlite-check-result ;
+
+: sqlite-bind-uint64 ( handle i n -- )
+    sqlite3-bind-uint64 sqlite-check-result ;
+
+: sqlite-bind-boolean ( handle name obj -- )
+    >boolean 1 0 ? sqlite-bind-int ;
+
+: sqlite-bind-double ( handle i x -- )
+    sqlite3_bind_double sqlite-check-result ;
+
+: sqlite-bind-null ( handle i -- )
+    sqlite3_bind_null sqlite-check-result ;
+
+: sqlite-bind-blob ( handle i byte-array -- )
+    dup length SQLITE_TRANSIENT
+    sqlite3_bind_blob sqlite-check-result ;
+
+: sqlite-bind-text-by-name ( handle name text -- )
+    parameter-index sqlite-bind-text ;
+
+: sqlite-bind-int-by-name ( handle name int -- )
+    parameter-index sqlite-bind-int ;
+
+: sqlite-bind-int64-by-name ( handle name int64 -- )
+    parameter-index sqlite-bind-int64 ;
+
+: sqlite-bind-uint64-by-name ( handle name int64 -- )
+    parameter-index sqlite-bind-uint64 ;
+
+: sqlite-bind-boolean-by-name ( handle name obj -- )
+    >boolean 1 0 ? parameter-index sqlite-bind-int ;
+
+: sqlite-bind-double-by-name ( handle name double -- )
+    parameter-index sqlite-bind-double ;
+
+: sqlite-bind-blob-by-name ( handle name blob -- )
+    parameter-index sqlite-bind-blob ;
+
+: sqlite-bind-null-by-name ( handle name obj -- )
+    parameter-index drop sqlite-bind-null ;
+
+: sqlite-finalize ( handle -- ) sqlite3_finalize sqlite-check-result ;
+: sqlite-reset ( handle -- ) sqlite3_reset sqlite-check-result ;
+: sqlite-clear-bindings ( handle -- )
+    sqlite3_clear_bindings sqlite-check-result ;
+: sqlite-#columns ( query -- int ) sqlite3_column_count ;
+: sqlite-column ( handle index -- string ) sqlite3_column_text ;
+: sqlite-column-name ( handle index -- string ) sqlite3_column_name ;
+: sqlite-column-type ( handle index -- string ) sqlite3_column_type ;
+
+: sqlite-column-blob ( handle index -- byte-array/f )
+    [ sqlite3_column_bytes ] 2keep
+    pick zero? [
+        3drop f
+    ] [
+        sqlite3_column_blob swap memory>byte-array
+    ] if ;
+
+: sqlite-step-has-more-rows? ( prepared -- ? )
+    {
+        { SQLITE_ROW [ t ] }
+        { SQLITE_DONE [ f ] }
+        [ sqlite-check-result f ]
+    } case ;
+
+: sqlite-next ( prepared -- ? )
+    sqlite3_step sqlite-step-has-more-rows? ;
+
+ERROR: sqlite-last-id-fail ;
+
+: last-insert-id ( -- id )
+    db-connection get handle>> sqlite3_last_insert_rowid
+    dup zero? [ sqlite-last-id-fail ] when ;
diff --git a/basis/sqlite/db2/queries/authors.txt b/basis/sqlite/db2/queries/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/sqlite/db2/queries/queries.factor b/basis/sqlite/db2/queries/queries.factor
new file mode 100644 (file)
index 0000000..436a761
--- /dev/null
@@ -0,0 +1,30 @@
+! Copyright (C) 2010 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors arrays db2.connections db2.queries db2.statements
+db2.types kernel math namespaces sequences sqlite.db2.connections ;
+IN: sqlite.db2.queries
+
+TUPLE: sqlite-object < sql-object table-type internal-name table-name rootpage sql ;
+TUPLE: sqlite-column < sql-column cid name type notnull dflt_value pk ;
+
+M: sqlite-db-connection current-db-name 
+    db-connection get db>> path>> ;
+
+: sqlite-table-info-statement ( string -- statement )
+    [ <statement> ] dip
+        sanitize-string
+        "pragma table_info('" "');" surround >>sql ;
+
+M: sqlite-db-connection sql-object-class sqlite-object ;
+M: sqlite-db-connection sql-column-class sqlite-column ;
+M: sqlite-db-connection databases-statement { } ;
+
+M: sqlite-db-connection database-table-columns-statement
+    nip
+    sqlite-table-info-statement
+        { INTEGER VARCHAR VARCHAR INTEGER VARCHAR INTEGER } >>out ;
+
+M: sqlite-db-connection database-tables-statement
+    drop
+    <statement>
+        "SELECT * FROM sqlite_master" >>sql ;
diff --git a/basis/sqlite/db2/result-sets/authors.txt b/basis/sqlite/db2/result-sets/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/sqlite/db2/result-sets/result-sets.factor b/basis/sqlite/db2/result-sets/result-sets.factor
new file mode 100644 (file)
index 0000000..77030d9
--- /dev/null
@@ -0,0 +1,27 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors db2.result-sets db2.statements destructors
+kernel sqlite.db2.connections sqlite.db2.lib sqlite.db2.types
+strings ;
+IN: sqlite.db2.result-sets
+
+TUPLE: sqlite-result-set < result-set has-more? ;
+
+M: sqlite-result-set dispose
+    f >>handle drop ;
+
+M: sqlite-db-connection statement>result-set
+    dup handle>>
+    sqlite-result-set new-result-set dup advance-row ;
+
+M: sqlite-result-set advance-row ( result-set -- )
+    dup handle>> sqlite-next >>has-more? drop ;
+
+M: sqlite-result-set more-rows? ( result-set -- ? )
+    has-more?>> ;
+
+M: sqlite-result-set #columns ( result-set -- n )
+    handle>> sqlite-#columns ;
+
+M: sqlite-result-set column ( result-set n type -- obj )
+    [ handle>> ] 2dip sqlite-type ;
diff --git a/basis/sqlite/db2/statements/authors.txt b/basis/sqlite/db2/statements/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/sqlite/db2/statements/statements.factor b/basis/sqlite/db2/statements/statements.factor
new file mode 100644 (file)
index 0000000..946063c
--- /dev/null
@@ -0,0 +1,21 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors db2.connections sqlite.db2.connections
+sqlite.db2.ffi sqlite.db2.lib db2.statements destructors kernel
+namespaces ;
+IN: sqlite.db2.statements
+
+M: sqlite-db-connection prepare-statement* ( statement -- statement )
+    db-connection get handle>> over sql>> sqlite-prepare
+    >>handle ;
+
+M: sqlite-db-connection reset-statement
+    [ handle>> sqlite3_reset drop ] keep ;
+
+M: sqlite-db-connection dispose-statement
+    handle>>
+    [ [ sqlite3_reset drop ] [ sqlite-finalize ] bi ] when* ;
+
+M: sqlite-db-connection next-bind-index "?" ;
+
+M: sqlite-db-connection init-bind-index ;
diff --git a/basis/sqlite/db2/tests/authors.txt b/basis/sqlite/db2/tests/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/sqlite/db2/tests/tests.factor b/basis/sqlite/db2/tests/tests.factor
new file mode 100644 (file)
index 0000000..4f59903
--- /dev/null
@@ -0,0 +1,24 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors db2 db2.statements db2.statements.tests db2.debug
+tools.test ;
+IN: sqlite.db2.tests
+
+: test-sql-bound-commands ( -- )
+    create-computer-table
+    
+    [ ] [
+        <statement>
+            "insert into computer (name, os, version) values(?, ?, ?);" >>sql
+            { "clubber" "windows" "7" } >>in
+        sql-command
+    ] unit-test
+
+    [ { { "windows" } } ] [
+        <statement>
+            "select os from computer where name = ?;" >>sql
+            { "clubber" } >>in
+        sql-query
+    ] unit-test ;
+
+[ test-sql-bound-commands ] test-sqlite
diff --git a/basis/sqlite/db2/types/authors.txt b/basis/sqlite/db2/types/authors.txt
new file mode 100644 (file)
index 0000000..b4bd0e7
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
\ No newline at end of file
diff --git a/basis/sqlite/db2/types/types.factor b/basis/sqlite/db2/types/types.factor
new file mode 100644 (file)
index 0000000..38866d4
--- /dev/null
@@ -0,0 +1,172 @@
+! Copyright (C) 2009 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors arrays calendar.format calendar.parser
+combinators db2.binders db2.statements db2.types db2.utils
+kernel math present sequences serialize sqlite.db2.connections
+sqlite.db2.ffi sqlite.db2.lib strings unicode urls ;
+IN: sqlite.db2.types
+
+: (bind-next-sqlite-type) ( handle key value type -- )
+    {
+        { INTEGER [ sqlite-bind-int ] }
+        { BIG-INTEGER [ sqlite-bind-int64 ] }
+        { SIGNED-BIG-INTEGER [ sqlite-bind-int64 ] }
+        { UNSIGNED-BIG-INTEGER [ sqlite-bind-uint64 ] }
+        { BOOLEAN [ sqlite-bind-boolean ] }
+        { TEXT [ sqlite-bind-text ] }
+        { VARCHAR [ sqlite-bind-text ] }
+        { DOUBLE [ sqlite-bind-double ] }
+        { REAL [ sqlite-bind-double ] }
+        { DATE [ timestamp>ymd sqlite-bind-text ] }
+        { TIME [ timestamp>hms sqlite-bind-text ] }
+        { DATETIME [ timestamp>ymdhms sqlite-bind-text ] }
+        { TIMESTAMP [ timestamp>ymdhms sqlite-bind-text ] }
+        { BLOB [ sqlite-bind-blob ] }
+        { FACTOR-BLOB [ object>bytes sqlite-bind-blob ] }
+        { URL [ present sqlite-bind-text ] }
+        { +db-assigned-key+ [ sqlite-bind-int ] }
+        { +random-key+ [ sqlite-bind-int64 ] }
+        { NULL [ drop sqlite-bind-null ] }
+        [ no-sql-type ]
+    } case ;
+
+: bind-next-sqlite-type ( handle key value type -- )
+    dup array? [ first ] when
+    over [
+        (bind-next-sqlite-type)
+    ] [
+        2drop sqlite-bind-null
+    ] if ;
+
+: (bind-sqlite-type) ( handle key value type -- )
+    dup array? [ first ] when
+    {
+        { INTEGER [ sqlite-bind-int-by-name ] }
+        { BIG-INTEGER [ sqlite-bind-int64-by-name ] }
+        { SIGNED-BIG-INTEGER [ sqlite-bind-int64-by-name ] }
+        { UNSIGNED-BIG-INTEGER [ sqlite-bind-uint64-by-name ] }
+        { BOOLEAN [ sqlite-bind-boolean-by-name ] }
+        { TEXT [ sqlite-bind-text-by-name ] }
+        { VARCHAR [ sqlite-bind-text-by-name ] }
+        { DOUBLE [ sqlite-bind-double-by-name ] }
+        { REAL [ sqlite-bind-double-by-name ] }
+        { DATE [ timestamp>ymd sqlite-bind-text-by-name ] }
+        { TIME [ timestamp>hms sqlite-bind-text-by-name ] }
+        { DATETIME [ timestamp>ymdhms sqlite-bind-text-by-name ] }
+        { TIMESTAMP [ timestamp>ymdhms sqlite-bind-text-by-name ] }
+        { BLOB [ sqlite-bind-blob-by-name ] }
+        { FACTOR-BLOB [ object>bytes sqlite-bind-blob-by-name ] }
+        { URL [ present sqlite-bind-text-by-name ] }
+        { +db-assigned-key+ [ sqlite-bind-int-by-name ] }
+        { +random-key+ [ sqlite-bind-uint64-by-name ] }
+        { NULL [ sqlite-bind-null-by-name ] }
+        [ no-sql-type ]
+    } case ;
+
+: bind-sqlite-type ( handle key value type -- )
+    ! null and empty values need to be set by sqlite-bind-null-by-name
+    over [
+        NULL = [ 2drop NULL NULL ] when
+    ] [
+        drop NULL
+    ] if* (bind-sqlite-type) ;
+
+: sql-type-unsafe ( handle index type -- obj )
+    {
+        { +db-assigned-key+ [ sqlite3_column_int64  ] }
+        { +random-key+ [ sqlite3-column-uint64 ] }
+        { INTEGER [ sqlite3_column_int ] }
+        { BIG-INTEGER [ sqlite3_column_int64 ] }
+        { SIGNED-BIG-INTEGER [ sqlite3_column_int64 ] }
+        { UNSIGNED-BIG-INTEGER [ sqlite3-column-uint64 ] }
+        { BOOLEAN [ sqlite3_column_int 1 = ] }
+        { DOUBLE [ sqlite3_column_double ] }
+        { REAL [ sqlite3_column_double ] }
+        { TEXT [ sqlite3_column_text ] }
+        { VARCHAR [ sqlite3_column_text ] }
+        { DATE [ sqlite3_column_text [ ymd>timestamp ] ?when ] }
+        { TIME [ sqlite3_column_text [ hms>duration ] ?when ] }
+        { TIMESTAMP [ sqlite3_column_text [ ymdhms>timestamp ] ?when ] }
+        { DATETIME [ sqlite3_column_text [ ymdhms>timestamp ] ?when ] }
+        { BLOB [ sqlite-column-blob ] }
+        { URL [ sqlite3_column_text [ >url ] ?when ] }
+        { FACTOR-BLOB [ sqlite-column-blob [ bytes>object ] ?when ] }
+        [ no-sql-type ]
+    } case ;
+
+ERROR: sqlite-type-error handle index type n ;
+
+: sqlite-type ( handle index type -- obj )
+    dup array? [ first ] when
+    2over sqlite-column-type {
+        { SQLITE_INTEGER [ sql-type-unsafe ] }
+        { SQLITE_FLOAT [ sql-type-unsafe ] }
+        { SQLITE_TEXT [ sql-type-unsafe ] }
+        { SQLITE_BLOB [ sql-type-unsafe ] }
+        { SQLITE_NULL [ 3drop f ] }
+        [ sqlite-type-error ]
+    } case ;
+
+GENERIC: sqlite-bind ( handle index obj -- )
+
+M: string sqlite-bind sqlite-bind-text ;
+M: in-binder-low sqlite-bind
+    [ value>> ] [ type>> ] bi bind-next-sqlite-type ;
+
+M: sqlite-db-connection bind-sequence ( statement -- )
+    [ in>> ] [ handle>> ] bi '[
+        [ _ ] 2dip 1 + swap sqlite-bind
+    ] each-index ;
+
+ERROR: no-fql-type type ;
+
+M: sqlite-db-connection db-type>fql-type ( string -- type )
+    >lower {
+        { "varchar" [ VARCHAR ] }
+        { "integer" [ INTEGER ] }
+        { "text" [ TEXT ] }
+        [ no-fql-type ]
+    } case ;
+
+M: sqlite-db-connection sql-type>string
+    dup array? [ first ] when
+    {
+        { INTEGER [ "INTEGER" ] }
+        { BIG-INTEGER [ "INTEGER " ] }
+        { SIGNED-BIG-INTEGER [ "BIGINT" ] }
+        { UNSIGNED-BIG-INTEGER [ "BIGINT" ] }
+        { DOUBLE [ "DOUBLE" ] }
+        { REAL [ "DOUBLE" ] }
+        { BOOLEAN [ "BOOLEAN" ] }
+        { TEXT [ "TEXT" ] }
+        { VARCHAR [ "TEXT" ] }
+        { DATE [ "DATE" ] }
+        { TIME [ "TIME" ] }
+        { DATETIME [ "DATETIME" ] }
+        { TIMESTAMP [ "TIMESTAMP" ] }
+        { BLOB [ "BLOB" ] }
+        { FACTOR-BLOB [ "BLOB" ] }
+        { URL [ "TEXT" ] }
+        { +db-assigned-key+ [ "INTEGER" ] }
+        { +random-key+ [ "INTEGER unique" ] }
+        [ no-sql-type ]
+    } case ;
+
+M: sqlite-db-connection sql-create-type>string sql-type>string ;
+
+: sqlite-modifier>string ( symbol -- string )
+    {
+        { NULL [ "NULL" ] }
+        { NOT-NULL [ "NOT NULL" ] }
+        { +not-null+ [ "NOT NULL" ] }
+        { SERIAL [ "SERIAL" ] }
+        { AUTOINCREMENT [ "AUTOINCREMENT" ] }
+        { +primary-key+ [ "" ] }
+        { +user-assigned-key+ [ "" ] }
+        { +db-assigned-key+ [ "" ] }
+        { +system-random-generator+ [ "" ] }
+        [ no-sql-modifier ]
+    } case ;
+
+M: sqlite-db-connection sql-modifiers>string
+    [ sqlite-modifier>string ] map " " join ;
diff --git a/basis/sqlite/orm/authors.txt b/basis/sqlite/orm/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/sqlite/orm/orm.factor b/basis/sqlite/orm/orm.factor
new file mode 100644 (file)
index 0000000..fe3f0d6
--- /dev/null
@@ -0,0 +1,9 @@
+! Copyright (C) 2010 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: sequences vocabs vocabs.loader ;
+IN: sqlite.orm
+
+[
+    "sqlite.orm.queries"
+    "sqlite.orm.types"
+] [ require ] each
diff --git a/basis/sqlite/orm/queries/authors.txt b/basis/sqlite/orm/queries/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/sqlite/orm/queries/queries.factor b/basis/sqlite/orm/queries/queries.factor
new file mode 100644 (file)
index 0000000..92bee8c
--- /dev/null
@@ -0,0 +1,177 @@
+! Copyright (C) 2010 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors arrays assocs combinators db2 db2.binders
+db2.query-objects db2.statements db2.types db2.utils fry kernel
+make namespaces orm.persistent orm.queries multiline
+sequences sqlite.db2.connections sqlite.db2.lib orm.binders ;
+IN: sqlite.orm.queries
+
+/*
+M: sqlite-db-connection reset-bind-index
+    0 \ bind-index set ;
+
+M: sqlite-db-connection next-bind-index
+    \ bind-index [ get ] [ inc ] bi number>string ;
+*/
+
+
+/*
+: insert-trigger-not-null ( -- string )
+    [
+    """
+        CREATE TRIGGER fki_${table-name}_${table-id}_${foreign-table-name}_${foreign-table-id}_id
+        BEFORE INSERT ON ${table-name}
+        FOR EACH ROW BEGIN
+            SELECT RAISE(ROLLBACK, 'insert on table "${table-name}" violates foreign key constraint "fki_${table-name}_$table-id}_${foreign-table-name}_${foreign-table-id}_id"')
+            WHERE NEW.${table-id} IS NOT NULL
+                AND (SELECT ${foreign-table-id} FROM ${foreign-table-name} WHERE ${foreign-table-id} = NEW.${table-id}) IS NULL;
+        END;
+    """ interpolate
+    ] with-string-writer ;
+
+: update-trigger ( -- string )
+    [
+    """
+        CREATE TRIGGER fku_${table-name}_${table-id}_${foreign-table-name}_${foreign-table-id}_id
+        BEFORE UPDATE ON ${table-name}
+        FOR EACH ROW BEGIN
+            SELECT RAISE(ROLLBACK, 'update on table "${table-name}" violates foreign key constraint "fku_${table-name}_$table-id}_${foreign-table-name}_${foreign-table-id}_id"')
+            WHERE (SELECT ${foreign-table-id} FROM ${foreign-table-name} WHERE ${foreign-table-id} = NEW.${table-id}) IS NULL;
+        END;
+    """ interpolate
+    ] with-string-writer ;
+
+: update-trigger-not-null ( -- string )
+    [
+    """
+        CREATE TRIGGER fku_${table-name}_${table-id}_${foreign-table-name}_${foreign-table-id}_id
+        BEFORE UPDATE ON ${table-name}
+        FOR EACH ROW BEGIN
+            SELECT RAISE(ROLLBACK, 'update on table "${table-name}" violates foreign key constraint "fku_${table-name}_$table-id}_${foreign-table-name}_${foreign-table-id}_id"')
+            WHERE NEW.${table-id} IS NOT NULL
+                AND (SELECT ${foreign-table-id} FROM ${foreign-table-name} WHERE ${foreign-table-id} = NEW.${table-id}) IS NULL;
+        END;
+    """ interpolate
+    ] with-string-writer ;
+
+: delete-trigger-restrict ( -- string )
+    [
+    """
+        CREATE TRIGGER fkd_${table-name}_${table-id}_${foreign-table-name}_${foreign-table-id}_id
+        BEFORE DELETE ON ${foreign-table-name}
+        FOR EACH ROW BEGIN
+            SELECT RAISE(ROLLBACK, 'delete on table "${foreign-table-name}" violates foreign key constraint "fkd_${table-name}_$table-id}_${foreign-table-name}_${foreign-table-id}_id"')
+            WHERE (SELECT ${foreign-table-id} FROM ${foreign-table-name} WHERE ${foreign-table-id} = OLD.${foreign-table-id}) IS NOT NULL;
+        END;
+    """ interpolate
+    ] with-string-writer ;
+
+: delete-trigger-cascade ( -- string )
+    [
+    """
+        CREATE TRIGGER fkd_${table-name}_${table-id}_${foreign-table-name}_${foreign-table-id}_id
+        BEFORE DELETE ON ${foreign-table-name}
+        FOR EACH ROW BEGIN
+            DELETE from ${table-name} WHERE ${table-id} = OLD.${foreign-table-id};
+        END;
+    """ interpolate
+    ] with-string-writer ;
+*/
+
+! : can-be-null? ( -- ? ) "sql-spec" get modifiers>> [ +not-null+ = ] any? not ;
+
+! : delete-cascade? ( -- ? ) "sql-spec" get modifiers>> { +on-delete+ +cascade+ } swap subseq? ;
+
+! : sqlite-trigger, ( string -- ) { } { } <simple-statement> 3, ;
+
+: sqlite-create-table ( tuple-class -- string )
+    >persistent dup table-name>>
+    [
+        [
+            [ columns>> ] dip
+            "CREATE TABLE " % %
+            "(" % [ ", " % ] [
+                [ column-name>> % " " % ]
+                [ type>> sql-create-type>string % ]
+                [
+                    modifiers>> sql-modifiers>string
+                    [ " " % % ] unless-empty
+                ] tri
+            ] interleave
+        ] [
+            drop
+            find-primary-key [
+                ", " %
+                "PRIMARY KEY(" %
+                [ "," % ] [ column-name>> % ] interleave
+                ")" %
+            ] unless-empty
+            ");" %
+        ] 2bi
+    ] "" make ;
+
+
+/*
+: create-sqlite-triggers ( -- )
+    can-be-null? [
+        insert-trigger sqlite-trigger,
+        update-trigger sqlite-trigger,
+    ] [
+        insert-trigger-not-null sqlite-trigger,
+        update-trigger-not-null sqlite-trigger,
+    ] if
+    delete-cascade? [
+        delete-trigger-cascade sqlite-trigger,
+    ] [
+        delete-trigger-restrict sqlite-trigger,
+    ] if ;
+
+: create-db-triggers ( sql-specs -- )
+    [ modifiers>> [ +foreign-id+ = ] deep-any? ] filter
+    [
+        [ class>> db-table-name "db-table" set ]
+        [
+            [ "sql-spec" set ]
+            [ column-name>> "table-id" set ]
+            [ ] tri
+            modifiers>> [ [ +foreign-id+ = ] deep-any? ] filter
+            [
+                [ second db-table-name "foreign-table-name" set ]
+                [ third "foreign-table-id" set ] bi
+                create-sqlite-triggers
+            ] each
+        ] bi
+    ] each ;
+*/
+
+M: sqlite-db-connection create-table-sql ( tuple-class -- seq )
+    ! [ sqlite-create-table ] [ drop create-db-triggers ] 2bi 2array ;
+    sqlite-create-table ;
+
+M: sqlite-db-connection ensure-table-sql ( tuple-class -- seq )
+    sqlite-create-table ;
+
+M: sqlite-db-connection insert-user-assigned-key-sql ( tuple -- object )
+    [ <statement> ] dip
+    [ >persistent ] [ ] bi {
+        [ drop table-name>> "INSERT INTO " "(" surround add-sql ]
+        [
+            filter-tuple-values
+            [
+                keys
+                [ [ column-name>> ] map ", " join ]
+                [
+                    length "?" <array> ", " join
+                    ") values(" ");" surround
+                ] bi append add-sql
+            ]
+            [ [ [ second ] [ first type>> ] bi <in-binder-low> ] map >>in ] bi
+        ]
+    } 2cleave ;
+
+M: sqlite-db-connection insert-db-assigned-key-sql
+    insert-user-assigned-key-sql ;
+
+M: sqlite-db-connection insert-tuple-set-key ( tuple statement -- )
+    sql-command last-insert-id set-primary-key drop ;
+
diff --git a/basis/sqlite/orm/types/authors.txt b/basis/sqlite/orm/types/authors.txt
new file mode 100644 (file)
index 0000000..7c1b2f2
--- /dev/null
@@ -0,0 +1 @@
+Doug Coleman
diff --git a/basis/sqlite/orm/types/types.factor b/basis/sqlite/orm/types/types.factor
new file mode 100644 (file)
index 0000000..623d267
--- /dev/null
@@ -0,0 +1,11 @@
+! Copyright (C) 2010 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors db2.result-sets kernel orm.binders
+sqlite.db2.types ;
+IN: sqlite.orm.types
+
+M: column-binder-in sqlite-bind
+    [ value>> ] [ column>> type>> ] bi bind-next-sqlite-type ;
+
+M: column-binder-out get-type
+    column>> type>> ;
diff --git a/basis/sqlite/sqlite.factor b/basis/sqlite/sqlite.factor
new file mode 100644 (file)
index 0000000..9cdd003
--- /dev/null
@@ -0,0 +1,9 @@
+! Copyright (C) 2010 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: sequences vocabs vocabs.loader ;
+IN: sqlite
+
+[
+    "sqlite.db2"
+    "sqlite.orm"
+] [ require ] each