The basis of our business has been writing compilers and run time
packages to integrate legacy and newer software. We discovered that
basic computer science provides the answers and looking beyond the
paradigm of the legacy language or system is essential if a complexity
chain reaction is to be avoided.
Good luck with your project.
Wesley W. Terpstra wrote:
On Feb 12, 2007, at 7:32 PM, John Stanton wrote:
I suggest that you also look carefully at the manifest typing
implemented in Sqlite. If your language is strongly typed you will
have some design issues to address.
I am aware of this issue and already have a solution. It's part of why
I wanted precompiled queries to be distinct from their execution state:
val AgeQuery = query db "select name, age from users where age="iI";"
oS oI $
will bind AgeQuery to a query factory that takes an integer as input
(the iI) and outputs a (string, integer) pair (from the oS oI). The '$'
is a terminating function.
This already works fine. Using a technique called functional unparsing,
the above line constructs a function (hidden behind the query type)
that converts+binds arguments and fetchs+converts result columms.
However, for some compiler implementations (not MLton), this dynamic
creation of a function is costly, so it should be done once per 'query
template'.
This binding should be good for more than just my favourite of the many
SML compilers.
sqlite3_exec ... implements callbacks and that you implement a
callback to handle each row. Then the logic of your interface is
elegant.
I already can do the functional equivalents. There is no real need for
a 'callback function' in a functional language. Functions can be passed
around like any other argument.
In the following code snippet, the function f takes the (string,
integer) pairs output by the query and creates a new string. The map
function takes the query result set and feeds it through this function.
The number '6' is the parameter which is bound to the query's input.
fun f (s & i) = "User " ^ s ^ " has age: " ^ Int.toString i
val results = SQL.map f AgeQuery 6
The value results will now contain a 'string vector' with entries like
"User Tom has age: 6", "User Joe has age: 6", ...
The problem I have stems from sqlite3_stmt containing both the parsed
query and the query state. In a binding like the above, these would
ideally happen at two distinct points.
1. query parsing happens in the 'val AgeQuery = query ...' line
2. query state is created in the 'SQL.map f AgeQuery' line
Then the user function 'f' can quite happily re-use AgeQuery to create
a recursive use of the query, but with different state (arguments and
stack).
Without this separation, I am forced to choose to do both at step 1 or
both at step 2:
1. both query state creation and parsing happen at 'val AgeQuery =
query ...'. This means that an attempt by 'f' to reuse AgeQuery must
result in an exception, as it would require two instances of the
AgeQuery state.
2. both query state creation and parsing happen at 'SQL.map f
AgeQuery'. This would allow 'f' to reuse AgeQuery, but it would mean
that every execution reparsed the query string. The documentation
implies this is unacceptably slow in some situations. As MLton- compiled
executables are about as fast as C [1], too slow for C is too slow for
SML.
If there were a separation between the execution state of a query, and
the prepared form of a query, then this would be a perfect fit. At the
moment, I take option #1. Now that I know SQLite can handle subqueries,
I would like to support this. It would be a shame to prohibit recursive
use of the same query.
I could also try hacking a hybrid approach. In this case, I mark the
AgeQuery as 'in-use' during the SQL.map. If a nested use of the query
occurs, I could clone the query, and use this clone for the nested
execution. From looking at the SQLite3 source code, it seems a clone
method would be relatively easy to add. I could also implement the
clone functionality in SML by re-executing prepare_v2 with a saved copy
of the query string.
You will find on reflection that the Sqlite API is simple and elegant
I didn't argue that it's inelegant. I *do* think it's a tad too simple.
The state of a query and the bytecode are combined into one concept. It
might be convenient most of the time, but it is a confusion of two
distinct things. You can run the same program more than once in Unix.
You can call the same method twice (even recursively) in every
programming language. I can't think of many places where code must be
one-to-one with data.
and will let you craft your interface provided you design it to suit
Sqlite, not have a preconceived notion and then be unable to make the
connection.
That's a valid point. I do have a preconceived notion of how this
should work. However, it comes from the standard idioms used in this
language, which (if at all possible) i would like to preserve.
[1] http://shootout.alioth.debian.org/
-----------------------------------------------------------------------------
To unsubscribe, send email to [EMAIL PROTECTED]
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
To unsubscribe, send email to [EMAIL PROTECTED]
-----------------------------------------------------------------------------