add example of using sqlite3 via pipes

FossilOrigin-Name: c5afff65edeeeff084caba6726c10369770fea6f5d8cd9676fb6f6e82f808573
This commit is contained in:
crc 2019-05-01 14:02:34 +00:00
parent bfe84ee73a
commit f3dd6271bf
5 changed files with 117 additions and 10 deletions

View file

@ -18,16 +18,7 @@ For most systems (FreeBSD, NetBSD, OpenBSD, macOS, Linux):
make
You will need a standard C compiler and `make`. The `retro-ri`
binary requires (n)curses, but you can ignore any build/link
errors by doing:
make -kis
If you are building on an older Linux system and are running
into problems, try using the alternative Makefile:
make -f Makefile.linux
You will need a standard C compiler and `make`.
## Executables
@ -77,6 +68,12 @@ fenced blocks:
Anything outside the fenced blocks will be ignored.
## Documentation
The primary documentation is in RETRO-Book.md (and the formatted
RETRO-Book.html.) Additional notes can be found in the `doc`
directory.
## Commercial Versions
I provide proprietary versions of RETRO for iOS and macOS.

View file

@ -20,3 +20,7 @@ This is the changelog for the development builds of Retro.
- updated Linux build instructions
- updated Starting instructions
## Examples
- add sqlite3 wrapper

64
example/sqlite3/sql.forth Normal file
View file

@ -0,0 +1,64 @@
This is an experimental set of words allowing use of sqlite3
within RETRO.
Do not use this in anything public facing. It doesn't do any
error checking, input validation, or make any attempt to be
remotely secure. It's also slow due to its design.
A lot of work is needed for this to actually be useful outside
of my tests.
This works by opening pipes to sqlite3, capturing the output,
and then processing it from within RETRO. This is slow, but
allows me to not need to deal with adding an FFI, and is
adaptable to similar external tools.
~~~
{{
'Database var
:setup here buffer:set ;
:pipe @Database 'sqlite3_%s_'%s' s:format file:R unix:popen ;
---reveal---
:sql:set-database s:keep !Database ;
:sql:query
[ setup pipe [ dup file:read dup buffer:add n:-zero? ] while unix:pclose ] buffer:preserve here ASCII:LF s:tokenize dup v:dec ;
:sql:split-line (s-a) $| s:tokenize ;
}}
~~~
# Construct an SQL Statement
~~~
{{
{ 'Action 'Choice 'Table 'Clause } [ var ] a:for-each
:clear { &Action &Choice &Table &Clause } [ v:off ] a:for-each ;
---reveal---
:SELECT 'SELECT !Action !Choice ;
:DROP 'DROP !Action ;
:FROM !Table ;
:WHERE !Clause ;
:sql:statement (q-s)
clear call @Clause n:-zero?
[ @Clause @Table @Choice @Action '%s_%s_FROM_%s_WHERE_%s ]
[ @Table @Choice @Action '%s_%s_FROM_%s ] choose
s:format ;
}}
~~~
# Generate Accessor Words
~~~
{{
'Table var
:set-table (s-s) dup s:keep !Table ;
:get-columns (s-a) 'PRAGMA_table\_info("%s") s:format sql:query ;
:id (a-n) #0 a:th fetch s:to-number ;
:column (a-s) #1 a:th fetch @Table '%s:%s s:format ;
:generate (ns-) d:create , [ fetch a:th fetch ] does ;
---reveal---
:sql:accessors-for (s-)
set-table get-columns
[ sql:split-line [ id ] [ column ] bi generate ] a:for-each ;
}}
~~~

BIN
example/sqlite3/test.db Normal file

Binary file not shown.

View file

@ -0,0 +1,42 @@
This is a test case, using an sqlite3 database to track time off
requests at my employer.
The database schema is:
CREATE TABLE pto(id integer primary key,
month blob, start blob, end blob, year blob, who blob,
reason blob, status blob);
Requests are a range, with a start and ending day. Requests for a single
day off have identical start and end days.
~~~
'sql.forth include (include_the_sqlite3_wrapper
'test.db sql:set-database (set_the_database_file
'pto sql:accessors-for (generate_accessors_for_each_column_in_"pto"
~~~
I can query for approved requests (status=y) in the current month.
~~~
[ 'pto FROM '* SELECT 'status="y"_AND_month=5_ORDER_BY_start WHERE ] sql:statement sql:query
~~~
And then iterate over the results, generating some readable output like:
Charles Childers
From: 2019-4-18 through 2019-4-22
(This part should be refactored to aid in readability, but this works
ok for a quick test.)
~~~
[ sql:split-line
dup pto:who s:put nl
[ &pto:start &pto:month &pto:year tri
'\tFrom:_%s-%s-%s_through_ s:format s:put ] sip
[ &pto:end &pto:month &pto:year tri
'%s-%s-%s\n s:format s:put ] sip
drop ] a:for-each
~~~