uSQLite

An SQLite network wrapper


What is it?

uSQLite is a network wrapper for SQLite. It turns SQLite into an RDBMS but puts the emphasis on the 'Lite'. In fact it works in a somewhat unconventional mmanner in order to make both servers and clients as light, portable and simple as possible. Readers who are not familiar with SQLite are advised to visit www.sqlite.org.

uSQLite uses the TechFell protocol for communications between clients and servers. This protocol was originally designed for allowing embedded systems to directly access a database, but has proved efficient in more general applications and in conjunction with a suitable server (such as uSQLite server) may also be used for inter-process and inter systems communications.

In automation systems it may be used to provide both database and communications in a single package, and in many cases can eliminate the need for communications middleware.

In a more general scenario it has the advantage that custom client software may be built into the applications (it is very easy to write and customize drivers) such that applications may be deployed without the need for a separate database / database driver layer to install and/or configure, in essence a distributed embedded database.


Features

Drawbacks



So is it a fork of SQLite or a new version?

Niether. It is a wrapper, an add-on adapter, in fact nearly all the functionality comes from a standard SQLite library. The program makes a minimalistic use of the SQLite C API  in the hope that users can link it against their desired SQLite library. Alternatively both SQLite and the uSQLite wrapper code may be linked into an application.


Why do I need this wrapper?

You probably don't. SQLite is an embedded database. Embedded in this sense has nothing to do with microcontrollers, it means that database functionality is built into the program such that it can directly access it's database files. If you have one program accessing a local database file then you probably do not need this (but see next question!).

When multiple programs need to access a database file, it is necessary to "lock" the file while it is being modified. Major platforms such as Windows and Macs have no problems here, but obscure systems may not be able to do this. Using a uSQLite server ensures that this is not a problem, but you will need to run the server as an additional process.

When sharing a file across a network locking problems are much more commonplace (see SQLite docs for what is known to work or not). If clients and servers are all on major platforms (*nix boxes, PC's windows, Mac etc.) you should perhaps be looking at a "real" RDBMS server like MySQL, Oracle or PostGres. If however you require the minimalistic requirements of SQLite then this wrapper is for you!

An alternative method of remote access to SQLite is to telnet into the sqlite console program that is supplied in the main SQLite distro. This works great but has two drawbacks. Firstly the interface is designed for human use, and it is not necessarily easy to implement in a program. The second drawback is that networking is not built in, you will require inetd or an equivalent program on non *nix platforms and you will rely on external access control. Also, an inetd solution requires a process for each client connection, which may be a problem on some limited systems (uSQLite allows you to limit the resources used by each client connection).


Why (else) might I need this wrapper?

SQLite has ready made bindings (libraries) that allow it to be used from most common languages. However, if your development tools are not supported, then you will need to implement your own. In many cases (particularly where it is not easy to make calls to external C libraries) you will probably find it easier to implement the TechFell protocol


What platform does it run on?

uSQLite can be compiled anywhere that SQLite can (which is most places), except that it also requires a TCP/IP stack. Berkeley sockets should run out of the box, but once again a minimalistic use of function calls has been made such that it is easy to adapt it to compile "not quite Berkeley" sockets. Winsock2 is already supported.

Client requirements are even simpler. All you need are streams to a TCP/IP network and the ability to manipulate and parse strings. Providing the sockets/streams are available it would be quite feasible to make a client using the macro/scripting language of many applications.


What's the difference between using uSQLite and SQlite proper?

Well obviously the interface is different! Also, you only get to use the Execute function, however SQLite's #PRAGMA commands allow complete control of the database. uSQLite adds a :PPRAGMA (pre-pragma) command which is used for uSQLite specific functionality such as access control, these commands are looped back directly. All connections to uSQLite use a single SQLite connection, so you cannot use transactions unless they BEGIN and END in the same query or group of queries sent as a single batch. In addition, temporary tables and memory based tables will appear to all users, this must be born in mind when naming temorary tables......some sort of prefixing convention may not be a bad idea.

Another limitation is the data that can be sent/received from the server. SQLite nominally uses ASCII strings but will accept most control characters. uSQLite on the other hand will not  handle newline characters, they are used as sync characters and as it does not scan or elaborate the internals of queries it cannot tell if they are quoted. The quick solution would be to escape them, for example C style '\r''s or to uuencode a field that may contain newline characters much as one would do with binary blobs. The primary reason for using newline characters do delimit packets (as apposed to say a 'Null') is that is makes the interface usable from environments that deal strictly with strings, which includes quite a lot of  'scriptable' environments. In the future there will be a 'NULL' mode (Try :PPRAGMA NULL to see if you already have it!), this mode will use a NULL instead of a newline on both input and output, so there is no newline problem but it is incompatible with telnet and strictly text environments.


Do ready made clients/libraries exist?

uSQLite is distributed with a VB client and a C client. The VB client is a very poor example, but it works. It implements a simple form which may be used to login to a database, execute queries, and review result sets. The form is invisible by default when loaded, and has some additional functions which allow it to be used as an interface library (the form become a db connection class!).

The Cclient is a portable C library which roughly mimics the SQLite exec command and callback function. Allthougth not a drop in replacement for the SQLite functions, it is easy to incorporate into existing programs which use the exec command. This library is used in an example application which consists of a command line utility which you launch with the query as an argument and get a result returned as a CSV type file. The connection parameters should be placed in environment variables, launch Cclient with no parameters to get a list of the environment variables required and the current values. See main.c for more info. The uSQLcClient.h file contains more info on using the library. Result go to stdout and messages to stderr, so you can pipe stdout to get a file.

There is also a CGI script written using the same C client library which allows queries to be made over HTTP.


And the License?

SQLite, which is what provides uSQLite with the bulk of it's functionality, has no license or copyright, just the following message:

** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**

This concept may be extended to uSQLiteServer and the supplied example clients, thus allowing me to comply with the 3rd article of the SQLite blessing. The TechFell protocol which uSQLite uses to communicate between database clients and servers is in the public domain, there is nothing to stop you rolling your own wrappers and clients. Both the uSQLiteServer and the client software are based on commonplace techniques and pratices that have been in use for at least 20 years, indeed in technology terms it represents a 'step backwards towards simplicity', and I sincerely hope that is not a patentable concept ;-)

BTW, at there time of writing there is no server source, which might sound ironic as the binaries exist. Truth is that the uSQLiteServer was incubated in an application which already had the TechFell protocol, but was hacked about with #ifdefs and all to get a stand alone server. My next task is to try and clean this up into a standalone app/library. This will be written in C and be as portable as possible (coding is similar to the Cclient example). The aim of the first server source release will be to have a clean and simple codebase which may be compiled on as many platforms as possible, I will not be adding functionality or changing the interface.


Credits

Apart from the obvious credit to SQLite, the following resources where used in the development of uSQLite:


Quickstart guide - The server

I would suggest that you do not try to modify the configurations or the contents of the db3 files until you have tried the examples, or they may not work!
Connections from localhost and 192.168.x.x will work out of the box, see security to configure for other networks.

The instructions are based on Windows, Linux is pretty much identical (use telnet for a text terminal, remember to configure for a linefeed after CR).
Linux executables have the same names as the windows ones, but without the .exe extension.

  1. Unpack the package in an empty directory, the application is staticly linked so their are no pre-requisites, setups or registrations to do.
  2. From a command shell in the root directory launch uSQLiteServer.exe uSQL.ini (Linux version has no .exe extension!), you should get 3 messages to tell you that the service is starting, the sec db as been opened, and the db has been opened.
  3. Launch a text terminal to connect to the server (a hyperterminal config may be found in db.ht)
  4. Log in and run queries, in the following session sample user supplied input is in bold and the response from the server is in italics. (You will need to be familiar with SQL and the TechFell protocol to understand what is going on:).
uSQLite 0.2
:OK
:PPRAGMA USER Level5

:PPRAGMA USER Level5
:OK
:PPRAGMA PASS newp2

:PPRAGMA USELEVEL 4
:OK
:PPRAGMA HUMAN

:PPRAGMA HUMAN MODE
:OK
select * from tst
====( 1 )====
foo = 10
baa = NULL
====( 2 )====
foo = 65
baa = This is a text field
====( 3 )====
foo = 20
baa = ::This field will be formalized in machine mode
:OK
:PPRAGMA MACHINE

:PPRAGMA MACHINE MODE
:OK
select * from tst
:H1:3 foo
:H2:3 baa
:R
10
!
65
This is a text field
20
:F47 ::This field will be formalized in machine mode
:OK



Security

You should read the section on Access control in the TechFell protocol first. The uSQLiteServer implementation uses a 'sec.db3' file. This table contains 2 tables. The networks table maps networks to maximum users levels using IP address and masks, the second table maps users to maximum user levels and stores a hash of the password. User levels are actually bit fields, and the netlevel will be AND'ed with the user level to get the effective level. Open it up and have a look (you will need a general purpose sqlite client here). It is pretty obvious.

As passwords are hashed you cannot set them using a normal sqlite client program, you must use the :PPRAGMA NEWPASS command via uSQLiteServer. New users should be added with blank passwords, to login the :PPRAGMA PASS command should be used with 1 space character after the PASS. You may also clear the password field to set a new password.

Note that using uSQLite means that the sec.db3 file (as well as the main db file) need only be visible to the server application, you do not need to export them on a network filesystem or make them accessible to any user other than the server process.


Configuration - the uSQL.ini file

The uSQL.ini file contains configuration parameters (yes, they will be in sqlite format in the future!). This a simple list of parameters, 1 per line (anything after the parameter id considered a comment and ignored.

The parameters must be given in the order shown. The uSQL.ini file supplied looks like this:

prime.db3    Main database
sec.db3        Sec database
3002        Service portnumber
10        Maxnumber of client connections
100000        Max size of query reply buffer (maxbuffer)
10000        Maximum size of records in reply (MaxRetRec)
4096        Maximum length of a query (MaxQlength) - To the server
10000        Maximum records in a reply (MaxRepRecs)
200        Init stored query start (o for none)
202        Init stored query end (=start for one query)

Most of the parameters are pretty obvious. Memory is dynamically allocated so there is no penalty for oversizing. The last 2 parameters are special. The database may contain a table called uSQL_P (there is an example in the supplied prime.db3). This table contains a list of SQL queries and an Index number. It is possible to execute a sequence of queries at start up in order to configure the database (why? see the section IPC Example). At startup, uSQLiteServer will execute "Select Query from uSQL_P where Idx between <start> and <end> order by Idx". It will then execute the queries in sequential order.

BTW, there is an unofficial :PPRAGMA EXEC <start> <end> command that does much the same.


IPC Example

A database is a convenient mechanism for inter process and inter system communications. Convenient, but not efficient, as data value changes get written back to the disk. This can be avoided by using memory based tables. In sqlite, memory based tables are only visible to the db connection which created them, however uSQLiteServer uses a single db connection for all clients, hence these tables will be visible to all uSQL clients, but not to other programs which open the db file.

Typically, it would be nice to have some disk based tables, and a few memory tables for IPC in the same database. This can be achieved by creating a memory table and using the attach command to join it to the main database schema. uSQLiteServer has an 'Init stored query' facility (see the Configuration section) that can do this set up automatically every time the server is started.

The supplied example configuration and database is configured to automatically generate a memory based table called tagvals based on information in the normal table tagdefs by automatically executing the following queries from uSQL_P:

Attach ':memory:' as mem
Create Table mem.Tagvals(Idx Integer Primary Key,Val Not Null,Err Not Null Default -1,Updated,WriteVal,WriteErr);
insert into Tagvals(Idx,Val) Select Idx,InitVal from TagDefs

Remember that one of the advantages of using SQL for IPC is that "polling" data may be selective. For example. if you need to poll the state of some alarms you need only poll the state of alarms which are active. Likewise if data is updated with a timestamps it is possible to query data which has changed since the time of the most recent update the client has received. Using selective queries not only allows a drastic reduction in the quantity of data which flies around, it may also reduce the amount of code you need to write.



Quickstart guide - The VB client example

Disclaimer. The VB example is not particularly good. If you VB experts out there would like to fix it or send something better......

Launch the executable and hit the "Launch a client" button. A client form will be loaded and made visible. Hit the connect button and the client will try to connect using the default parameters given in the text boxes at the top of the form. These should work with the supplied db, and you should see the login results in the lowwer panel. Clear the trash out of the upper pane and write a query like "select * from tagdefs", then hit the Exec button. The lowwer pane should automatically toggle to a grid when the result set arrives, this grid is VERY slow, it is not the server or the protocol but my poor VB skills!

You may launch as many client panes as you like, but note that the uSQL.ini file will have defined a limit on the max number of client connections.

The client form is invisible on creation, so you may create a form and use it as a connection class. There are a few functions which enable this. Hit the 'Updates' button and a new connection will be made which updates the allarm states (the black boxes on the right) every 50mS. DO NOT HIT THE Allarm BUTTONS YET! The 3rd button makes another connection object to connect the Allarm buttons. After you have clicked this you can toggle the allarm states by clicking the Allarm button. This does a round trip, there is no direct connection between the buttons and the allarm states, the button simply writes the 'opposite' of the Allarm state to the ActVal column of the corresponding record in tagdefs, this is then read by the other connection to update the display.