| Author: | Brian Tiffin |
|---|---|
| Date: | 09-Oct-2008 |
| Version: | 0.90 based on SQLite 3.5.9 |
| Rights: | Copyright (c) 2008, Brian Tiffin Licensed under the GNU Public License 2.0 ocsqlite.c is in the public domain in keeping with the original SQLite source code This Usage documentation licensed FDL |
| Purpose: | Demonstrate SQLite in OpenCOBOL |
| Needs: | libsqlite3-dev and sqlite3 packages or libsqlite and sqlite version 2 packages |
| Tectonics: | cobc -c ocsqlite.c cobc -x -lsqlite3 sqlscreen.cob ocsqlite.o |
| Docgen: | $ ./ocdoc sqlscreen.cob sqlscreen.rst sqlscreen.html ocfaq.css |
sqlscreen.cob demonstrates some of the features provided by the SQLite shell interface.
Requires sqlite3 and libsqlite3-dev packages (for Debian) Requires OC1.1 packaged on or later than Oct 13th 2008:
$ cobc -c ocshell.c -or- $ cobc -c -DSQLITE_OMIT_LOAD_EXTENSION ocshell.c $ cobc -x -lsqlite3 sqlcaller.cob ocshell.o -or- $ cobc -x -lsqlite3 -fdebugging-line sqlcaller.cob ocshell.o
All of the meta (dot) commands supported by the sqlite3> console are included in the ocsqlite.c interface.
A query of .help will list the allowed meta commands.
Many of the meta commands output results to stdout. A few of the commands can be dangerous, and may be disabled in the 1.0 release. Commands such as .bail on
SQLite includes nearly all SQL verbs and most database management commands. Full details can be found at http://www.sqlite.org/lang.html
The database name is passed as a zero terminated C string:
01 database pic x(8) value 'test.db' & x'00'.
The initialization is somewhat misleading. The db pointer is set, but the internal ocshell.c database pointer is used on all the queries:
call "ocsqlite_init" using
db
database
by reference errstr
by value function length(errstr)
returning result
end-call
if result not equal zero
display "Result: " result end-display
end-if
SQLite uses a callback for each row of a query:
set callback-proc to entry "callback"
And a lot of code only executes if sqlscreen.cob is compiled with -fdebugging-line:
>>Dmove ".echo on" to query
>>Dperform ocsql-exec
>>Dmove ".help" to query
>>Dperform ocsql-exec
>>Dmove ".tables" to query
>>Dperform ocsql-exec
>>Dmove ".timer on" to query
>>Dperform ocsql-exec
>>Dmove ".mode tcl" to query
>>Dperform ocsql-exec
>>Dmove 0 to row-counter
>>Dmove "select * from trial;" to query
>>Dperform ocsql-exec
>>Dmove ".mode html" to query
>>Dperform ocsql-exec
>>Dmove 'insert into trial values (null, "string", "2008-10-10");'
>>D to query
>>Dperform ocsql-exec
>>Dmove "select * from thisfails;" to query
>>Dperform ocsql-exec
move "drop table trial;" to query
perform ocsql-exec
move "create table trial (first integer primary key, " &
"second char(20), third date);" to query
perform ocsql-exec
>>Dmove "pragma count_changes=1;" to query
>>Dperform ocsql-exec
>>Dmove "pragma database_list;" to query
>>Dperform ocsql-exec
>>Dmove ".schema trial" to query
>>Dperform ocsql-exec
move 'insert into trial (first, second, third) values ' &
'(null, lower(hex(randomblob(20))), datetime()); ' &
'insert into trial values (null, "something",' &
' julianday());' to query
perform ocsql-exec
>>Dmove "select * from trial;" to query
>>Dperform ocsql-exec
>>Dmove "pragma count_changes=0;" to query
>>Dperform ocsql-exec
*><*
*><* Most of the default demo is here::
*><*
move 'insert into trial (first, second, third) values ' &
'(null, lower(hex(randomblob(20))), datetime()); ' &
'insert into trial values (null, "something",' &
' julianday());' to query
perform ocsql-exec
The .mode column and .width meta statements are critical for this example. The sqlite shell will now return fixed length fields. Breaking DRY, the widths must be explicitly set.
A .mode csv could be used with an unstring for variable length datums, but then quotes will need processing as well.
move ".mode column" to query
perform ocsql-exec
move ".width 10 20 20" to query
perform ocsql-exec
move 1 to row-counter
move "select * from trial;" to query
perform ocsql-exec
display function trim(sql-table trailing) end-display
subtract 1 from row-counter giving row-max end-subtract
perform varying row-counter from row-max by -1
until row-counter < 1
move sql-records(row-counter) to main-record
display "|" key-field "|" end-display
display "|" str-field "|" end-display
display "|" date-field "|" end-display
end-perform
Finally put up a screen, cycling through the records:
perform varying row-counter from 1 by 1
until row-counter > row-max
move sql-records(row-counter) to main-record
accept entry-screen end-accept
end-perform
Once again, the alpha release of ocshell.c keeps its own database handle. That will change. The close call is still necessary, just that the db handle is not used.:
call "ocsqlite_close"
using
by value db
returning result
end-call
move result to return-code
goback.
Callback procedure. In sqlite_exec, the callback is passed:
void *user_data, int fields, char **columns, char **names
for each row, with the name and value data in separatearrays.
The OpenCOBOL callback procedure is called with:
pointer, int fields, row as alphanum, row length
Each line of row data is formatted according to the shell's mode setting. The expectation is .mode column with fixed .width
In a demonstration of very bad form; if the external value of row-counter is larger than 0, fill the external sql-records structure:
move value-display to main-record
if row-counter > 0
move main-record to sql-records(row-counter)
add 1 to row-counter end-add
end-if
Cheers