sqlite: add ability to bind parameters by name.
sqlite: add simple tuple database routines sqlite: add ability to save a tuple sqlite: Add documentation for tuple-db sqlite: add note about closing databasecvs
parent
68ad2ef61f
commit
76cb0ae949
|
@ -0,0 +1,58 @@
|
|||
! Copyright (C) 2005 Chris Double.
|
||||
!
|
||||
! Redistribution and use in source and binary forms, with or without
|
||||
! modification, are permitted provided that the following conditions are met:
|
||||
!
|
||||
! 1. Redistributions of source code must retain the above copyright notice,
|
||||
! this list of conditions and the following disclaimer.
|
||||
!
|
||||
! 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
! this list of conditions and the following disclaimer in the documentation
|
||||
! and/or other materials provided with the distribution.
|
||||
!
|
||||
! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
!
|
||||
IN: tuple-db
|
||||
USING: io kernel kernel-internals sequences namespaces hashtables
|
||||
lists sqlite errors math words generic test ;
|
||||
|
||||
TUPLE: testdata one two ;
|
||||
|
||||
testdata default-mapping set-mapping
|
||||
|
||||
SYMBOL: db
|
||||
"test.db" sqlite-open db set
|
||||
|
||||
db get testdata create-tuple-table
|
||||
|
||||
[ "two" f ] [
|
||||
db get "one" "two" <testdata> insert-tuple
|
||||
db get "one" f <testdata> find-tuples
|
||||
first [ testdata-two ] keep
|
||||
db get swap delete-tuple
|
||||
db get "one" f <testdata> find-tuples
|
||||
] unit-test
|
||||
|
||||
[ "junk" ] [
|
||||
db get "one" "two" <testdata> insert-tuple
|
||||
db get "one" f <testdata> find-tuples
|
||||
first
|
||||
"junk" over set-testdata-two
|
||||
db get swap update-tuple
|
||||
db get "one" f <testdata> find-tuples
|
||||
first [ testdata-two ] keep
|
||||
db get swap delete-tuple
|
||||
] unit-test
|
||||
|
||||
db get testdata drop-tuple-table
|
||||
|
||||
db get sqlite-close
|
|
@ -0,0 +1,275 @@
|
|||
! Copyright (C) 2005 Chris Double.
|
||||
!
|
||||
! Redistribution and use in source and binary forms, with or without
|
||||
! modification, are permitted provided that the following conditions are met:
|
||||
!
|
||||
! 1. Redistributions of source code must retain the above copyright notice,
|
||||
! this list of conditions and the following disclaimer.
|
||||
!
|
||||
! 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
! this list of conditions and the following disclaimer in the documentation
|
||||
! and/or other materials provided with the distribution.
|
||||
!
|
||||
! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
!
|
||||
! A tuple that is persistent has its delegate set as 'persistent'.
|
||||
! 'persistent' holds the numeric rowid for that tuple in its table.
|
||||
IN: tuple-db
|
||||
USING: io kernel kernel-internals sequences namespaces hashtables
|
||||
lists sqlite errors math words generic ;
|
||||
|
||||
! Each slot in a tuple that is storable in the database has
|
||||
! an instance of a db-field object the gives the name of the
|
||||
! database table and slot number in the tuple object of that field.
|
||||
TUPLE: db-field name bind-name slot type ;
|
||||
|
||||
! The mapping tuple holds information on how the slots of
|
||||
! a tuple are mapped to the fields of a sqlite database.
|
||||
TUPLE: mapping tuple table fields one-to-one one-to-many ;
|
||||
|
||||
: tuple-fields ( class -- seq )
|
||||
#! Given a tuple class return a list of the fields
|
||||
#! within that tuple. Ignores the delegate field.
|
||||
[ word-name length 1+ ] keep
|
||||
"slots" word-prop 1 swap tail [ ( name-len @{ slot getter setter }@ )
|
||||
[ 1 swap nth word-name tail dup ":" swap append ] keep
|
||||
0 swap nth
|
||||
"text"
|
||||
<db-field>
|
||||
] map-with ;
|
||||
|
||||
: default-mapping ( class -- mapping )
|
||||
#! Given a tuple class, create a default mappings object. It assumes
|
||||
#! there are no one-to-one or one-to-many relationships.
|
||||
dup [ word-name ] keep tuple-fields f f <mapping> ;
|
||||
|
||||
! The mappings variable holds a hashtable mapping the tuple symbol
|
||||
! to the mapping object, describing how that tuple is stored
|
||||
! in the database.
|
||||
SYMBOL: mappings
|
||||
|
||||
: init-mappings ( -- )
|
||||
#!
|
||||
{{ }} mappings set ;
|
||||
|
||||
: set-mapping ( mapping -- )
|
||||
#! Store a database mapping so that the persistence system
|
||||
#! knows how to store instances of the relevant tuple in the database.
|
||||
dup mapping-tuple mappings get set-hash ;
|
||||
|
||||
: get-mapping ( class -- mapping )
|
||||
#! Return the database mapping for the given tuple class.
|
||||
mappings get hash ;
|
||||
|
||||
! The 'persistent' tuple will be set to the delegate of any tuple
|
||||
! instance stored in the database. It contains the database key
|
||||
! of the row in the database table for the instance or 'f' if it has
|
||||
! not yet been stored in the database. It also contains the 'mapping'
|
||||
! object used to translate the fields of the tuple to the database fields.
|
||||
TUPLE: persistent mapping key ;
|
||||
C: persistent ( tuple -- persistent )
|
||||
>r class-tuple get-mapping r>
|
||||
[ set-persistent-mapping ] keep ;
|
||||
|
||||
: make-persistent ( tuple -- tuple )
|
||||
#! Convert the tuple into something that can be stored
|
||||
#! into a database by setting its delegate to 'persistent'.
|
||||
[ <persistent> ] keep
|
||||
[ set-delegate ] keep ;
|
||||
|
||||
|
||||
: comma-fields ( mapping quot -- string )
|
||||
#! Given a mapping, call quot on each field in
|
||||
#! the mapping. The contents of quot should call ',' or '%'
|
||||
#! to generate output. The output of each quot call
|
||||
#! seperated by commas is returned as a string. 'quot' should be
|
||||
#! stack effect ( field -- ).
|
||||
swap mapping-fields [
|
||||
swap "" make
|
||||
] map-with "," join ; inline
|
||||
|
||||
GENERIC: create-sql
|
||||
M: mapping create-sql ( mapping -- string )
|
||||
#! Return the SQL used to create a table for storing this type of tuple.
|
||||
[
|
||||
"create table " % dup mapping-table %
|
||||
" (" %
|
||||
[ dup db-field-name % " " % db-field-type % ] comma-fields %
|
||||
");" %
|
||||
] "" make ;
|
||||
|
||||
GENERIC: drop-sql
|
||||
M: mapping drop-sql ( mapping -- string )
|
||||
#! Return the SQL used to drop the table for storing this type of tuple.
|
||||
[
|
||||
"drop table " % mapping-table % ";" %
|
||||
] "" make ;
|
||||
|
||||
GENERIC: insert-sql
|
||||
M: mapping insert-sql ( mapping -- string )
|
||||
#! Return the SQL used to insert a tuple into a table
|
||||
[
|
||||
"insert into " % dup mapping-table %
|
||||
" values(" %
|
||||
[ db-field-bind-name % ] comma-fields %
|
||||
");" %
|
||||
] "" make ;
|
||||
|
||||
GENERIC: delete-sql
|
||||
M: mapping delete-sql ( mapping -- string )
|
||||
#! Return the SQL used to delete a tuple from a table
|
||||
[
|
||||
"delete from " % mapping-table %
|
||||
" where ROWID=:rowid;" %
|
||||
] "" make ;
|
||||
|
||||
GENERIC: update-sql
|
||||
M: mapping update-sql ( mapping -- string)
|
||||
#! Return the SQL used to update the tuple
|
||||
[
|
||||
"update " % dup mapping-table %
|
||||
" set " %
|
||||
[ dup db-field-name % "=" % db-field-bind-name % ] comma-fields %
|
||||
" where ROWID=:rowid;" %
|
||||
] "" make ;
|
||||
|
||||
GENERIC: select-sql
|
||||
M: mapping select-sql ( tuple mapping -- select )
|
||||
#! Return the SQL used to select a series of tuples from the database. It
|
||||
#! will select based on only the filled in fields of the tuple (ie. all non-f).
|
||||
[
|
||||
"select ROWID,* from " % dup mapping-table %
|
||||
" where " %
|
||||
mapping-fields [ ( tuple field )
|
||||
swap over db-field-slot slot ( field value )
|
||||
[
|
||||
[ dup db-field-name % "=" % db-field-bind-name % ] "" make
|
||||
] [
|
||||
drop f
|
||||
] if
|
||||
] map-with [ ] subset " and " join %
|
||||
";" %
|
||||
] "" make ;
|
||||
|
||||
: execute-update-sql ( db string -- )
|
||||
#! Execute the SQL, which should contain a database update
|
||||
#! statement (update, insert, create, etc). Ignore the result.
|
||||
sqlite-prepare dup [ drop ] sqlite-each sqlite-finalize ;
|
||||
|
||||
: create-tuple-table ( db class -- )
|
||||
#! Create the table for the tuple class.
|
||||
get-mapping create-sql execute-update-sql ;
|
||||
|
||||
: drop-tuple-table ( db class -- )
|
||||
#! Create the table for the tuple class.
|
||||
get-mapping drop-sql execute-update-sql ;
|
||||
|
||||
: bind-for-insert ( statement tuple -- )
|
||||
#! Bind the fields in the tuple to the fields in the
|
||||
#! prepared insert statement.
|
||||
dup class-tuple get-mapping mapping-fields [ ( statement tuple field )
|
||||
[ db-field-slot slot ] keep ( statement value field )
|
||||
db-field-bind-name swap ( statement name value )
|
||||
>r dupd r> sqlite-bind-text-by-name
|
||||
] each-with drop ;
|
||||
|
||||
: bind-for-select ( statement tuple -- )
|
||||
#! Bind the fields in the tuple to the fields in the
|
||||
#! prepared select statement.
|
||||
dup class-tuple get-mapping mapping-fields [ ( statement tuple field )
|
||||
[ db-field-slot slot ] keep ( statement value field )
|
||||
over [
|
||||
db-field-bind-name swap ( statement name value )
|
||||
>r dupd r> sqlite-bind-text-by-name
|
||||
] [
|
||||
2drop
|
||||
] if
|
||||
] each-with drop ;
|
||||
|
||||
: bind-for-update ( statement tuple -- )
|
||||
#! Bind the fields in the tuple to the fields in the
|
||||
#! prepared update statement.
|
||||
2dup bind-for-insert
|
||||
>r ":rowid" r> persistent-key sqlite-bind-text-by-name ;
|
||||
|
||||
: bind-for-delete ( statement tuple -- )
|
||||
#! Bind the fields in the tuple to the fields in the
|
||||
#! prepared delete statement.
|
||||
>r ":rowid" r> persistent-key sqlite-bind-text-by-name ;
|
||||
|
||||
: (insert-tuple) ( db tuple -- )
|
||||
#! Insert this tuple instance into the database. Note that
|
||||
#! it inserts only this instance, and not any one-to-one or
|
||||
#! one-to-many fields.
|
||||
dup class-tuple get-mapping insert-sql ( db tuple sql )
|
||||
swapd sqlite-prepare swap ( statement tuple )
|
||||
dupd bind-for-insert ( statement )
|
||||
dup [ drop ] sqlite-each
|
||||
sqlite-finalize ;
|
||||
|
||||
: insert-tuple ( db tuple -- )
|
||||
#! Insert this tuple instance into the database and
|
||||
#! update the rowid of the insert in the tuple.
|
||||
[ (insert-tuple) ] 2keep
|
||||
>r sqlite-last-insert-rowid number>string r> make-persistent set-persistent-key ;
|
||||
|
||||
: update-tuple ( db tuple -- )
|
||||
#! Update this tuple instance in the database. The tuple should have
|
||||
#! a delegate of 'persistent' with the key field set.
|
||||
dup class-tuple get-mapping update-sql ( db tuple sql )
|
||||
swapd sqlite-prepare swap ( statement tuple )
|
||||
dupd bind-for-update ( statement )
|
||||
dup [ drop ] sqlite-each
|
||||
sqlite-finalize ;
|
||||
|
||||
: save-tuple ( db tuple -- )
|
||||
#! Insert or Update the tuple instance depending on whether it
|
||||
#! has a persistent delegate.
|
||||
dup delegate [ update-tuple ] [ insert-tuple ] if ;
|
||||
|
||||
: delete-tuple ( db tuple -- )
|
||||
#! Delete this tuple instance from the database. The tuple should have
|
||||
#! a delegate of 'persistent' with the key field set.
|
||||
dup class-tuple get-mapping delete-sql ( db tuple sql )
|
||||
swapd sqlite-prepare swap ( statement tuple )
|
||||
dupd bind-for-delete ( statement )
|
||||
dup [ drop ] sqlite-each
|
||||
sqlite-finalize ;
|
||||
|
||||
: restore-tuple ( statement tuple -- tuple )
|
||||
#! Using 'tuple' as a template, clone it and
|
||||
#! return the clone with fields set to the values from the
|
||||
#! database.
|
||||
clone dup class-tuple get-mapping mapping-fields 1 swap
|
||||
[ ( statement tuple index field )
|
||||
over 1+ >r ( statement tuple index field r: index+1 )
|
||||
db-field-slot >r ( statement tuple index r: index+1 slot )
|
||||
pick swap column-text ( statement tuple value r: index+1 slot )
|
||||
over r> set-slot r> ( statement tuple index+1 )
|
||||
] each ( statement tuple index )
|
||||
drop make-persistent swap 0 column-text swap [ set-persistent-key ] keep ;
|
||||
|
||||
: find-tuples ( db tuple -- seq )
|
||||
#! Return a sequence of all tuples in the database that
|
||||
#! match the tuple provided as a template. All fields in the
|
||||
#! tuple must match the entries in the database, except for
|
||||
#! those set to 'f'.
|
||||
dup class-tuple get-mapping dupd select-sql ( db tuple sql )
|
||||
swapd sqlite-prepare swap ( statement tuple )
|
||||
2dup bind-for-select ( statement tuple )
|
||||
f pick [ ( tuple accum statement )
|
||||
pick restore-tuple swons
|
||||
] sqlite-each ( statement tuple accum )
|
||||
rot sqlite-finalize nip ;
|
||||
|
||||
|
||||
mappings get [ init-mappings ] unless
|
|
@ -0,0 +1,232 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Tuple Database Library</title>
|
||||
<style>
|
||||
body { background: white; color: black; }
|
||||
p { margin-left: 10%; margin-right: 10%;
|
||||
font: normal 100% Verdana, Arial, Helvetica; }
|
||||
td { margin-left: 10%; margin-right: 10%;
|
||||
font: normal 100% Verdana, Arial, Helvetica; }
|
||||
table { margin-left: 10%; margin-right: 10%; }
|
||||
ul { margin-left: 10%; margin-right: 10%;
|
||||
font: normal 100% Verdana, Arial, Helvetica; }
|
||||
ol { margin-left: 10%; margin-right: 10%;
|
||||
font: normal 100% Verdana, Arial, Helvetica; }
|
||||
h1 { text-align: center; margin-bottom: 0; margin-top: 1em; }
|
||||
h2 { margin: 0 5% 0 7.5%; font-size: 120%; font-style: italic; }
|
||||
h3 { border: 2px solid blue; border-width: 2px 0.5em 2px 0.5em;
|
||||
padding: 0.2em 0.2em 0.2em 0.5em; background: #fafafa;
|
||||
margin-left: 10%; margin-right: 10%; margin-top: 2em;
|
||||
font-size: 100%; }
|
||||
.note { border: 2px solid blue; border-width: 2px 2px 2px 2em;
|
||||
padding: 0.5em 0.5em 0.5em 1em; background: #ffe; }
|
||||
.code { border: 1px solid black; border-width: 1px;
|
||||
padding: 0.5em; background: #ffe;
|
||||
margin-left: 10%; margin-right: 10%; }
|
||||
blockquote { margin-left: 25%; margin-right: 25%;
|
||||
font-style: italic; }
|
||||
.highlite { color: red; }
|
||||
.footer { margin-top: 2.5em; border-top: 1px solid gray; color:
|
||||
#AAA; font-size: 85%; padding-top: 0.33em; }
|
||||
#copyright { text-align: center; color: #AAA;
|
||||
font-size: 65%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Tuple Database Library</h1>
|
||||
<h1>Overview</h1>
|
||||
<p class="note">The version of sqlite supported by this library is
|
||||
version 3 or greater.</p>
|
||||
<p>This library allows storing Factor tuples in a sqlite database. It
|
||||
provides words to create, read update and delete these entries as well
|
||||
as simple searching.</p>
|
||||
<p>The library is in a very early state and is likely to change quite
|
||||
a bit in the near future. Its most notable omission is it cannot currently
|
||||
handle relationships between tuples. This feature is currently being
|
||||
worked on.</p>
|
||||
<h1>Loading</h1>
|
||||
<p>The Factor image must have been bootstrapped with the
|
||||
sqlite shared library name provided. This can be done with the
|
||||
following command:</p>
|
||||
<pre class="code">
|
||||
./f boot.image.le32 -libraries:sqlite:name=libsqlite3.so
|
||||
</pre>
|
||||
<p>The quickest way to get up and running with this library is to
|
||||
change to the 'sqlite' directory and run Factor. Then execute the
|
||||
following commands:</p>
|
||||
<pre class="code">
|
||||
"sqlite.factor" run-file
|
||||
"tuple-db.factor" run-file
|
||||
USE: sqlite
|
||||
USE: tuple-db
|
||||
</pre>
|
||||
<p>Some simple tests can be run to check that everything is working
|
||||
ok:</p>
|
||||
<pre class="code">
|
||||
"tuple-db-tests.factor" run-file
|
||||
</pre>
|
||||
<h1>Basic Usage</h1>
|
||||
<p>This library can be used for storing simple Factor tuples in a
|
||||
sqlite database. In its current form the tuples must not contain
|
||||
references to other tuples and should not have a delegate set.</p>
|
||||
<p>This document will use the following tuple for demonstration
|
||||
purposes:</p>
|
||||
<pre class="code">
|
||||
TUPLE: person name surname phone ;
|
||||
</pre>
|
||||
<p>The sqlite database to store tuples must be created, or an existing
|
||||
one opened. This
|
||||
is done using the 'sqlite-open word. If the database does not exist
|
||||
then it is created. The examples in this document
|
||||
store the database pointer in a variable called 'db':</p>
|
||||
<pre class="code">
|
||||
SYMBOL: db
|
||||
"example.db" sqlite-open db set
|
||||
</pre>
|
||||
<h2>Tuple Mappings</h2>
|
||||
<p>Each tuple has a 'mapping' tuple associated with it. The 'mapping'
|
||||
stores information about what table the tuple will be stored in, the
|
||||
datatypes of the tuple slots, etc. A mapping must be created before a
|
||||
tuple can be stored in a database. A default mapping is easily created
|
||||
using the 'default-mapping' word. Given the tuple class, this will use
|
||||
reflection to get the slots of it, assume that all slots are of
|
||||
database type 'text', and store the tuple objects in a table with the
|
||||
same name as the tuple.</p>
|
||||
<p>The following shows how to create the default mapping for the
|
||||
'person' tuple, and how to register that mapping so the 'tuple-db'
|
||||
system can know how to handle 'person' instances:</p>
|
||||
<pre class="code">
|
||||
person default-mapping set-mapping
|
||||
</pre>
|
||||
<h2>Creating the table</h2>
|
||||
<p>The table used to store tuple instances may need to be
|
||||
created. This can be done manually using 'sqlite' or via the
|
||||
'create-tuple-table' word:</p>
|
||||
<pre class="code">
|
||||
<span class="highlite">! create-tuple-table ( db class -- )</span>
|
||||
db get person create-tuple-table
|
||||
</pre>
|
||||
<p>The SQL used to create the table is produced by the 'create-sql'
|
||||
word. This is a generic word dispatched on the mapping object, and
|
||||
could be specialised if needed. If you wish to see the SQL used to
|
||||
create the table, use the following code:</p>
|
||||
<pre class="code">
|
||||
<span class="highlite">! M: mapping create-sql ( mapping -- string )</span>
|
||||
person get-mapping create-sql .
|
||||
=> "create table person (name text,surname text,phone text);"
|
||||
</pre>
|
||||
<h2>Inserting instances</h2>
|
||||
<p>The 'insert-tuple' word will store instances of a tuple
|
||||
into the database table defined by its mapping object. It's as
|
||||
simple as:</p>
|
||||
<pre class="code">
|
||||
<span class="highlite">! insert-tuple ( db tuple -- )</span>
|
||||
db get "John" "Smith" "123-456-789" <person> insert-tuple
|
||||
</pre>
|
||||
<p>'insert-tuple' internally uses the 'insert-sql' word to produce
|
||||
the SQL used to store the tuple. Like 'create-sql', 'insert-sql' is
|
||||
a generic word specialized on the mapping object. You can call it
|
||||
directly to see what SQL is generated:</p>
|
||||
<pre class="code">
|
||||
<span class="highlite">! M: mapping insert-sql ( mapping -- string )</span>
|
||||
person get-mapping insert-sql .
|
||||
=> "insert into person values(:name,:surname,:phone);"
|
||||
</pre>
|
||||
<p>Notice that the SQL uses named parameters. This parameters are bound to the values stored in the tuple object when the SQL is compiled. This helps prevent SQL injection techniques.</p>
|
||||
<p>When the 'insert-sql' word is run, it adds a delegate to the tuple being stored. The delegate is of type 'persistent' and holds the row id of the tuple in its 'key' slot. This way the exact record can be updated or retrieved later. The following demonstates this fact:</p>
|
||||
<pre class="code">
|
||||
"Mandy" "Jones" "987-654-321" <person> dup .
|
||||
=> << person f "Mandy" "Jones" "987-654-321" >>
|
||||
db get over insert-tuple .
|
||||
=> << person
|
||||
<< persistent ... <span class="highlite">"2"</span> >>
|
||||
"Mandy" "Jones" "987-654-321"
|
||||
>>
|
||||
</pre>
|
||||
<p>The '2' highlited in the above example is the row id of the record inserted. We can go into the 'sqlite' command and view this record:</p>
|
||||
<pre class="code">
|
||||
$ sqlite3 example.db
|
||||
SQLite version 3.0.8
|
||||
Enter ".help" for instructions
|
||||
sqlite> select ROWID,* from person;
|
||||
1|John|Smith|123-456-789
|
||||
<span class="highlite">2|Mandy|Jones|987-654-321</span>
|
||||
sqlite>
|
||||
</pre>
|
||||
<h2>Finding Instances</h2>
|
||||
<p>The 'find-tuples' word is used to return tuples populated with data already
|
||||
existing in the database. As well as the database pointer, it takes a tuple that should be populated only with the fields that should be matched in the database. All fields you do not wish to match against should be set to 'f':</p>
|
||||
<pre class="code">
|
||||
<span class="highlite">! find-tuples ( db tuple -- seq )</span>
|
||||
db get f "Smith" f <person> find-tuples short.
|
||||
=> [ << person # "John" "Smith" "123-456-789" >> ]
|
||||
db get "Mandy" f f <person> find-tuples short.
|
||||
=> [ << person # "Mandy" "Jones" "987-654-321" >> ]
|
||||
db get "Joe" f f <person> find-tuples short.
|
||||
=> f
|
||||
</pre>
|
||||
<p>Notice that if no matching tuples are found then 'f' is returned. The returned tuples also have their delegate set to 'persistent' with the correct row id set as the key. This can be used to later update the tuples with new information and store them in the database.</p>
|
||||
<h2>Updating Instances</h2>
|
||||
<p>Given a tuple that has the 'persistent' delegate with the row id
|
||||
set as the key, you can update this specific record using the
|
||||
'update-tuple' word:</p>
|
||||
<pre class="code">
|
||||
<span class="highlite">! update-tuple ( db tuple -- )</span>
|
||||
db get f "Smith" f <person> find-tuples dup short.
|
||||
=> [ << person # "John" "Smith" "123-456-789" >> ]
|
||||
first [ "999-999-999" swap set-person-phone ] keep dup short.
|
||||
=> << person << persistent f # "1" >> "John" "Smith" "999-999-999" ...
|
||||
db get swap update-tuple
|
||||
</pre>
|
||||
<p>Using the 'sqlite' command from the system shell you can see the
|
||||
record was updated:</p>
|
||||
<pre class="code">
|
||||
$ sqlite3 example.db
|
||||
SQLite version 3.0.8
|
||||
Enter ".help" for instructions
|
||||
sqlite> select ROWID,* from person;
|
||||
1|John|Smith|999-999-999
|
||||
<span class="highlite">2|Mandy|Jones|987-654-321</span>
|
||||
sqlite>
|
||||
</pre>
|
||||
<h2>Inserting or Updating</h2>
|
||||
<p>The 'save-tuple' word can be used to insert a tuple if it has not
|
||||
already been stored in the database, or update it if it already
|
||||
exists. Whether to insert or update is decided by the existance of the
|
||||
'persistent' delegate:</p>
|
||||
<pre class="code">
|
||||
<span class="highlite">! save-tuple ( db tuple -- )</span>
|
||||
"Mary" "Smith" "111-111-111" <person> dup short.
|
||||
=> << person f "Mary" "Smith" "111-111-111" >>
|
||||
! This will insert the tuple
|
||||
db get over save-tuple dup short.
|
||||
=> << person << persistent f # "3" >> "Mary" "Smith" "111-111-111" ...
|
||||
[ "222-222-222" swap set-person-phone ] keep dup short.
|
||||
=> << person << persistent f # "3" >> "Mary" "Smith" "222-222-222" ...
|
||||
! This will update the tuple
|
||||
db get over save-tuple short.
|
||||
=> << person << persistent f # "3" >> "Mary" "Smith" "222-222-222" ...
|
||||
</pre>
|
||||
<h2>Deleting</h2>
|
||||
<p>Given a tuple with the delegate set to 'persistent' (ie. One
|
||||
already stored in the database) you can delete it from the database
|
||||
with the 'delete-tuple' word:</p>
|
||||
<pre class="code">
|
||||
<span class="highlite">! delete-tuple ( db tuple -- )</span>
|
||||
db get f "Smith" f <person> find-tuples [
|
||||
db get swap delete-tuple
|
||||
] each
|
||||
</pre>
|
||||
<h2>Closing the database</h2>
|
||||
<p>It's important to close the sqlite database when you've finished
|
||||
using it. The word for this is 'sqlite-close':</p>
|
||||
<pre class="code">
|
||||
<span class="highlite">! sqlite-close ( db -- )</span>
|
||||
db get sqlite-close
|
||||
</pre>
|
||||
|
||||
<p class="footer">
|
||||
News and updates to this software can be obtained from the authors
|
||||
weblog: <a href="http://radio.weblogs.com/0102385">Chris Double</a>.</p>
|
||||
<p id="copyright">Copyright (c) 2004, Chris Double. All Rights Reserved.</p>
|
||||
</body> </html>
|
Loading…
Reference in New Issue