Re: TDBC documentation, examples, syntax?
- From: Kevin Kenny <kennykb@xxxxxxx>
- Date: Sun, 22 Feb 2009 16:53:00 -0500
tom.rmadilo wrote:
The simplest type of reflection is "pass through semantics". With pass
through semantics, function names remain the same and argument order
remains the same.
If you just change the function name, while the signature remains the
same, you can use a "mapping".
What I see in the TDBC API is a distinct, un-mapped API.
Yes, you do. There is no a simple mapping of TDBC onto SQL/CLI,
just as there is no simple mapping of SQL/Object or SQL/JRT onto
SQL/CLI. It is a distinct API. That is intentional, not accidental.
Departing from SQL/CLI's function names and signatures is a conscious
decision. Let me attempt to make a comparison between the one-to-one
mapping that you imagine and what TDBC actually does.
Let's consider the (admittedly trivial) problem of extracting a
telephone number from a 'people' table given the person's surname
and given name. (To preempt an expected digression: I *realize* that
it's not ISO/IEC 9594 [X.500] compliant. It's an *example*.)
The SQL query will be:
select telephone from directory
where given_name = :given and family_name = :family
where the colon denotes variable substitution by name.
TDBC tries to hide all the machinery of variable binding
from the user, and a small wrapper around this query might
look like:
package require tdbc::odbc
set given Mickey
set family Mouse
tdbc::odbc::connection create db \
"DSN=DirectoryDatabase;UID=aladdin;PWD=sesame"
$db foreach row {
select telephone from directory
where given_name = :given and family_name = :family
} {
dict with $row { puts $telephone }
}
$db close
This is a complete program! The TDBC calls number only three:
one to open a connection, one to execute the query and iterate
over the results, and one to close the connection. This
structure is typical of the minimalism common to many of Tcl's
APIs.
By contrast, doing the same thing with SQL/CLI would require:
(1) a call to SQLAllocHandle to get an environment handle.
(2) a second call to SQLAllocHandle to get a connection handle.
(3) a call to SQLConnect, SQLDriverConnect, or SQLBrowseConnect
to establish the connection.
(4) a third call to SQLAllocHandle to get a statement handle.
(5) a call to SQLPrepare to prepare the statement. (Prepared
statements, or at least bound paramenters, are needed for
safety. We don't want a visit from Little Bobby Tables.)
(6,7) Two calls to SQLBindParameter, one for 'given' and one
for 'family'. Note that this call has ten parameters,
and obtaining correct values for some of them probably
requires at least some interactions with SQLGetTypeInfo
to introspect upon the available data types.
(8) A call to SQLExecute to release the statement to the
database.
(If the SQL isn't fixed but comes from caller input:)
(9) A call to SQLGetNumResultColumns to determine how many
result columns need to be bound. (One.)
(10) A call to SQLDescribeCol to find the name and data type
of the column.
(In any case:)
(11) A call to SQLFetch per row, to retrieve the result data.
(12) A call to SQLGetData per cell, to retrieve the actual result
content. In the case of string results, the call to SQLGetData
may need to be repeated to deal with SQLSTATE==01004
"buffer too small".
(13) A call to SQLCloseCursor to dismiss the result set.
(14) A call to SQLFreeHandle to dismiss the statement handle.
(15) A call to SQLFreeHandle to dismiss the connection handle.
(16) A call to SQLFreeHandle to dismiss the environment handle.
This is a minimum of fourteen calls (a minimum of sixteen if
the SQL is generated dynamically) instead of three. Several
of the calls, notably SQLBindParameter and SQLFetch, have long
positional parameter lists. Moreover, all parameters and results
have to be specified by numeric position; if the statement to
be executed is at all complex, considerable programmer bookkeeping
is required to count the positions manually.
Perhaps you believe that the advantage of familiarity would
trump the simplicity of the TDBC approach. I differ. Following
the ISO standard by a strict mapping would be a disaster for the
Tcl programmer.
Anyway, get a clue: any layer code needs two interfaces (one up, one
down). At most I see one semi-explicit upper interface and one non-
explicit lower interface.
I presume that you call it "semi-explicit" because the method
of obtaining a connection is left unspecified. The last is intentional,
because when dealing with API's other than SQL/CLI (ODBC), it is
impossible to enumerate in advance the parameters that might be
required to describe a connection.
The "lower interface" also is left unspecified at the level of
the detailed SQL/CLI calls to be done, because it's
there for vendor-specific database API's. If a database uses
standard SQL/CLI, then tdbc::odbc is already available, and works
with it. And it's open source. Why on Earth would you want
to reimplement it? And if you're not reimplementing it, why
do you need a formal specification of *how* to implement it?
Since such a formal specification would, by its
very nature, rule out dealing with vendor-specific API's,
it would rather defeat the purpose of TDBC. If all I wanted was
a Tcl binding for SQL/CLI, I'd simply have gone and fixed some
of the bugs in the 'tclodbc' extension and called it a day.
So what *is* specified of the "lower interface" is simply a
subset of the "upper interface". There must *be* a connection
object, and that object must either implement the entire
connection object interface, or else inherit from tdbc::connection,
set the 'statementClass' instance variable, and implement the
'prepareCall', 'begintransaction', 'commit', 'rollback',
'tables' and 'columns' methods (and the constructor and
destructor).
The statement object (whose class name is in 'statementClass'
-- or which is returned from 'prepare' and 'prepareCall')
must either implement the entire statement interface as
described, or else inherit from tdbc::statement, set the
'resultSetClass variable' and implement the constructor.
destructor and the 'paramtype' method.
The result set object (whose class name is in 'resultSetClass'
-- or which is returned from the 'execute' method) must
either implement the entire result set interface as described,
or else inherit from tdbc::resultSet, and implement the
constructor, the destructor, and the methods, 'columns',
'nextdict', 'nextlist', and 'rowcount'.
So a complete TDBC driver defines three classes and implements
eleven methods (plus three constructors and three destructors).
(It may, of course also need additional classes and methods as part
of its internal machinery; that's no business of TDBC's.)
That's really all there is. Perhaps you have been led astray
by the minimalism of the API into thinking that necessary things
have been left out. If so, then be explicit: state what has been
left out, and we can talk about adding it if necessary. But be
advised that tdbc's developers consider the minimal interface
to be a feature, not a bug.
And lay off on the "get a clue" language. We'll make a lot more
progress if we can deal from a foundation of mutual respect.
--
73 de ke9tv/2, Kevin
.
- Follow-Ups:
- Re: TDBC documentation, examples, syntax?
- From: newtophp2000
- Re: TDBC documentation, examples, syntax?
- References:
- TDBC documentation, examples, syntax?
- From: stolicow@xxxxxxxxx
- Re: TDBC documentation, examples, syntax?
- From: suchenwi
- Re: TDBC documentation, examples, syntax?
- From: tom.rmadilo
- Re: TDBC documentation, examples, syntax?
- From: George Peter Staplin
- Re: TDBC documentation, examples, syntax?
- From: stevel
- Re: TDBC documentation, examples, syntax?
- From: tom.rmadilo
- Re: TDBC documentation, examples, syntax?
- From: Kevin Kenny
- Re: TDBC documentation, examples, syntax?
- From: tom.rmadilo
- TDBC documentation, examples, syntax?
- Prev by Date: Re: TDBC documentation, examples, syntax?
- Next by Date: Re: HTTPS access
- Previous by thread: Re: TDBC documentation, examples, syntax?
- Next by thread: Re: TDBC documentation, examples, syntax?
- Index(es):
Relevant Pages
|