tdbc::tokenize documentation and use

Viewed 62

I'm using tdbc::odbc to connect to a Pervasive (btrieve type) database, and am unable to pass variables to the driver. A short test snippet:

set customer "100000"
set st [pvdb prepare {
    INSERT INTO CUSTOMER_TEMP_EMPTY
    SELECT * FROM CUSTOMER_MASTER
    WHERE CUSTOMER = :customer
}]
$st execute

This returns:

[Pervasive][ODBC Client Interface]Parameter number out of range. (binding the 'customer' parameter)

Works fine if I replace :customer with "100000", and I have tried using a variable with $, @, wrapping in apostrophes, quotes, braces. I believe that tdbc::tokenize is the answer I'm looking for, but the man page gives no useful information on its use. I've experimented with tokenize with no progress at all. Can anyone comment on this?

2 Answers

The tdbc::tokenize command is a helper for writing TDBC drivers. It's used for working out what bound variables are inside an SQL string so the binding map can be supplied to the low level driver or, in the case of particularly stupid drivers, string substitutions performed (I hope there's no drivers that need to do this; it'd be annoyingly difficult to get right). The parser knows enough to handle weird cases like things that look like bound variables in strings and comments (those aren't bound variables).

If we feed it (it's calling syntax is trivial) the example SQL you've got, we get this result:

{
    INSERT INTO CUSTOMER_TEMP_EMPTY
    SELECT * FROM CUSTOMER_MASTER
    WHERE CUSTOMER = } :customer {
}

That's a list of three items (the last element just has a newline in it) that simplifies processing a lot; each item is either trivially a bound variable or trivially not.

Other examples (bear in mind in the second case that bound variables may also start with $ or @):

% tdbc::tokenize {':abc' = :abc = ":abc" -- :abc}
{':abc' = } :abc { = ":abc" -- :abc}
% tdbc::tokenize {foo + $bar - @grill}
{foo + } {$bar} { - } @grill
% tdbc::tokenize {foo + :bar + [:grill]}
{foo + } :bar { + [:grill]}

Note that the tokenizer does not fully understand SQL! It makes no attempt to parse the other bits; it's just looking for what is a bound variable.


I've no idea what use the tokenizer could be to you if you're not writing a DB driver.

Still could not get the driver to accept the variable, but looking at your first example of the tokenized return, I came up with:

set customer "100000"
set v [tdbc::tokenize "$customer"]
set query "INSERT INTO CUSTOMER_TEMP_EMPTY SELECT * FROM CUSTOMER_MASTER WHERE CUSTOMER = $v"
set st [pvdb prepare $query]
$st execute

as a test command, and it did indeed successfully pass the command through the driver

Related