ODBC distinguishes between direct execution of literal SQL strings and parameterized execution of SQL strings. The first is a simple practical solution for infrequent calls (such as creating a table), while parameterized execution allows the driver and database to precompile the query and store the optimized code, making it suitable for time-critical operations. In addition, it allows for passing parameters without going through SQL-syntax and thus avoiding the need for quoting.
[]
for Options.
If the statement is a SELECT
statement the result-set is
returned in RowOrAffected. By default rows are returned
one-by-one on backtracking as terms of the functor row/Arity
,
where Arity denotes the number of columns in the result-set.
The library pre-fetches the next value to be able to close the statement
and return deterministic success when returning the last row of the
result-set. Using the option findall/2
(see below) the
result-set is returned as a list of user-specified terms. For other
statements this argument returns affected(Rows)
, where Rows
represents the number of rows affected by the statement. If you are not
interested in the number of affected rows odbc_query/2
provides a simple interface for sending SQL-statements.
Below is a small example using the connection created from
odbc_connect/3.
Please note that the SQL-statement does not end in the‘
’character.
;
lemma(Lemma) :- odbc_query(wordnet, 'SELECT (lemma) FROM word', row(Lemma)).
The following example adds a name to a table with parent-relations,
returning the number of rows affected by the statement. Note that the
SQL quote character is the ASCII single quote and, as this SQL
quote is embedded in a single quoted Prolog atom, it must be written as \'
or ''
(two single quotes). We use the first
alternative for better visibility.
insert_child(Child, Mother, Father, Affected) :- odbc_query(parents, 'INSERT INTO parents (name,mother,father) \ VALUES (\'mary\', \'christine\', \'bob\')', affected(Affected)).
Options defines the following options.
default
to use
default conversion for that column. The length of the type-list must
match the number of columns in the result-set.
For example, in the table word
the first column is
defined with the SQL type DECIMAL(6)
. Using this SQL-type, “001” is
distinct from “1” , but using Prolog integers is a valid
representation for Wordnet wordno
identifiers. The
following query extracts rows using Prolog integers:
?- odbc_query(wordnet, 'select * from word', X, [ types([integer,default]) ]). X = row(1, entity) ; X = row(2, thing) ; ...
See also section 2.7 for notes on type-conversion.
true
(default false
), include the
source-column with each result-value. With this option, each result in
the
row/N
-term is of the format below. TableName
or
ColumnName may be the empty atom if the information is not
available.3This is one possible
interface to this information. In many cases it is more efficient and
convenient to provide this information separately as it is the same for
each result-row.
column(TableName, ColumnName, Value)
lemmas(Lemmas) :- findall(Lemma, odbc_query(wordnet, 'select (lemma) from word', row(Lemma)), Lemmas).
Using the findall/2
option the above can be implemented
as below. The number of argument of the row
term must match
the number of columns in the result-set.
lemmas(Lemmas) :- odbc_query(wordnet, 'select (lemma) from word', Lemmas, [ findall(Lemma, row(Lemma)) ]).
The current implementation is incomplete. It does not
allow arguments of
row(...)
to be instantiated. Plain instantiation can always
be avoided using a proper SELECT statement. Potentially useful however
would be the translation of compound terms, especially to translate
date/time/timestamp structures to a format for use by the application.
SELECT
). The predicate prints a
diagnostic message if the query returns a result.
ODBC provides for‘parameterized queries’. These are SQL
queries with a
-sign at places where
parameters appear. The ODBC interface and database driver may use this
to precompile the SQL-statement, giving better performance on repeated
queries. This is exactly what we want if we associate Prolog predicates
to database tables. This interface is defined by the following
predicates:
?
[]
for Options.?
)
and unify Statement with a handle to the created statement. Parameters
is a list of descriptions, one for each parameter. Each parameter
description is one of the following:
silent(true)
option
of odbc_set_connection/2.
An alternative mapping can be selected using the > option
of this predicate described below.char
, varchar
,
etc. to specify the field-width. When calling odbc_execute/[2-3], the
user must supply the parameter values in the default Prolog type for
this SQL type. See section 2.7 for
details.atom > date
The use must supply an atom of the format YYYY-MM-DD
rather than a term date(Year,Month,Day)
. This construct
enhances flexibility and allows for passing values that have no proper
representation in Prolog.
default
. It unifies Variable with
the
PrologType > SqlType as using the
types derived.4 The current
version does not provide the field with in SqlType. Future
versions may improve on that. This feature is first of all
intended for debugging. Using odbc_debug(1)
, the library
prints details on the derived types.Options defines a list of options for executing the statement. See odbc_query/4 for details. In addition, the following option is provided:
auto
(default) to extract the result-set on backtracking or fetch
to prepare the result-set to be fetched using odbc_fetch/3.
ODBC doesn't appear to allow for multiple cursors on the same
result-set.5Is this right?
This would imply there can only be one active odbc_execute/3
(i.e. with a choice-point) on a prepared statement. Suppose we have a
table age (name char(25), age integer)
bound to the
predicate age/2 we cannot write the code
below without special precautions. The ODBC interface therefore creates
a clone of a statement if it discovers the statement is being executed,
which is discarded after the statement is finished.6The
code is prepared to maintain a cache of statements. Practice should tell
us whether it is worthwhile activating this.
same_age(X, Y) :- age(X, AgeX), age(Y, AgeY), AgeX = AgeY.
Normally SQL queries return a result-set that is enumerated on backtracking. Using this approach a result-set is similar to a predicate holding facts. There are some cases where fetching the rows one-by-one, much like read/1 reads terms from a file is more appropriate and there are cases where only part of the result-set is to be fetched. These cases can be dealt with using odbc_fetch/3, which provides an interface to SQLFetchScroll().
As a general rule of thumb, stay away from these functions if you do not really need them. Experiment before deciding on the strategy and often you'll discover the simply backtracking approach is much easier to deal with and about as fast.
fetch(fetch)
and be executed using odbc_execute/2. Row
is unified to the fetched row or the atom end_of_file
7This
atom was selected to emphasise the similarity to read.
after the end of the data is reached. Calling odbc_fetch/2
after all data is retrieved causes a permission-error exception. Option
is one of:
relative(1)
is the same
as next
, except that the first row extracted is row 2.
In many cases, depending on the driver and RDBMS, the cursor-type
must be changed using odbc_set_connection/2
for anything different from next
to work.
Here is example code each time skipping a row from a table‘test’holding a single column of integers that represent the row-number. This test was executed using unixODBC and MySQL on SuSE Linux.
fetch(Options) :- odbc_set_connection(test, cursor_type(static)), odbc_prepare(test, 'select (testval) from test', [], Statement, [ fetch(fetch) ]), odbc_execute(Statement, []), fetch(Statement, Options). fetch(Statement, Options) :- odbc_fetch(Statement, Row, Options), ( Row == end_of_file -> true ; writeln(Row), fetch(Statement, Options) ).
Most SQL queries return only a single result set - a list of rows. However, some queries can return more than one result set. For example,’SELECT 1; SELECT 2’is a batch query that returns a single row (1) and then a single row(2). Queries involving stored procedures can easily generate such results.
To retrieve data from a subsequent result set, odbc_next_result_set/1 can be used, but only for prepared queries which were prepared with fetch(fetch) as the fetch style in the option list.
fetch(Options) :- odbc_prepare(test, 'select (testval) from test; select (anotherval) from some_other_table', [], Statement, [ fetch(fetch) ]), odbc_execute(Statement, []), fetch(Statement, Options). fetch(Statement, Options) :- odbc_fetch(Statement, Row, Options), ( Row == end_of_file -> ( odbc_next_result_set(Statement) -> writeln(next_result_set), fetch(Statement, Options) ; true ) ; writeln(Row), fetch(Statement, Options) ).