All known outright incompatibilities between KInterbasDB and the Python Database API Specification 2.0 have been fixed in KInterbasDB 3.0. However, some optional features are not implemented (or are only nominally implemented) due to limitations in the Interbase/Firebird database engine (see below).
Cursor
class
nextset
methodThis method is not implemented because the database engine does not support opening multiple result sets simultaneously with a single cursor.
rowcount
attribute
The value of this attribute is initially -1
,
and it never changes,
because the Interbase/Firebird C API does not support the
determination of
the number of rows affected by an executed statement.
Cursor
class
arraysize
attribute
As required by the spec, the value of this attribute
is observed with respect to the fetchmany
method. However, changing the value of this attribute does
not make any difference in fetch efficiency because the
database engine only supports fetching a single row at a time.
setinputsizes
methodAlthough this method is present, it does nothing, as allowed by the spec.
setoutputsize
methodAlthough this method is present, it does nothing, as allowed by the spec.
Note that the performance of BLOB retrieval has been greatly enhanced in KInterbasDB 3.0; the implementation now behaves in such a way that calling this method would almost never offer any advantage even if it were implemented.
connect
function
This function supports the following optional arguments in addition to those required by the spec:
role
-
for connecting to a database with a specific SQL role
(see page 92 of the Interbase 6 Operations Guide for a
discussion of Interbase roles).
Example:
kinterbasdb.connect(dsn="host:/path/database.gdb", user="limited_user", password="pass", role="MORE_POWERFUL_ROLE")
charset
-
for explicitly specifying the character set of the connection.
Example:
kinterbasdb.connect(dsn="host:/path/database.gdb", user="sysdba", password="masterkey", charset="UNICODE_FSS")
dialect
-
for explicitly specifying the SQL dialect of the connection.
In KInterbasDB 2.x, the default dialect was 1
(the compatibility dialect for Interbase 5.5 and earlier).
In KInterbasDB 3.0, the default dialect is 3
(the most featureful dialect, ideal for Interbase 6.0+
and Firebird).
If you want to connect to Interbase 5.5 or earlier, you must
explicitly set this argument's value to 1
.
Example:
kinterbasdb.connect(dsn="host:/path/database.gdb", user="sysdba", password="masterkey", dialect=1)
create_database
function
Creates a database according to the supplied
CREATE DATABASE
SQL statement.
Returns an open connection to the newly created database.
Arguments:
sql
-
string containing the CREATE DATABASE
statement.Note that you may need to specify a username and password as part of this statement (see the Interbase/Firebird SQL Reference for syntax).
dialect
(optional) -
the SQL dialect under which to execute the statement
(defaults to 3
).
Connection
class
drop_database
methodDeletes the database to which the connection is attached.
This method performs the database deletion in a responsible fashion. Specifically, it
OperationalError
instead of deleting
the database if there are other active connections to the
databaseThis method has no arguments.
dialect
attributeThis integer attribute indicates which SQL dialect the connection is using.
The value of this attribute can be changed dynamically, but that
is not a recommended programming practice. Instead, you should
specify the optional dialect
argument of the
connect
function when you first create the connection.
For more information, see the documentation of the
dialect
argument of the
connect
function.
precision_mode
attribute
Binary floating point representation of fractional numbers is
often slightly imprecise. For this reason, Interbase/Firebird
offers
floating point datatypes
to be used where a slight
lack of precision is tolerable
(FLOAT
and DOUBLE PRECISION
)
and
fixed point datatypes
to be used where perfect (though finite) precision is required
(NUMERIC
and DECIMAL
).
The representation of currency values is a typical situation in which perfect precision is demanded. For the gory details of the fixed point datatypes, see the section of the Interbase 6 Data Definition Guide entitled "Fixed-decimal datatypes" (page 64).
Unfortunately, Python currently lacks a built-in fixed point type. Those who wish to convert between the database engine's fixed point types and native Python types face a choice:
For example,
on a typical 32-bit platform, the fractional number
12.345
stored in the database in a
NUMERIC(5,3)
field becomes the Python float
12.345000000000001
. The Python programmer
can then use this value in a natural way, but some
precision is lost.
For example, the fractional number
12.345
stored in the database in a
NUMERIC(5,3)
field becomes the Python
integer 12345
. The Python programmer
must take into account the fact that this value is scaled
by three decimal places. (Note that the scale factor can
be determined programmatically by examining
cur.description[fieldPosition][kinterbasdb.DESCRIPTION_PRECISION]
.)
KInterbasDB 2.x and earlier always represented the database's
fixed point values as scaled Python integers. Via the
Connection.precision_mode
attribute,
KInterbasDB 3.0 offers the Python programmer a choice between the
two representation schemes--between convenience and precision.
When a connection's precision_mode
is 0
(the default), approach #1 is used;
when precision_mode
is
1
, approach #2 is used.
Keep in mind that the precision_mode
affects input
as well as output. In precision_mode
0
, placing the Python integer 12
in a NUMERIC(5,3)
field will result in a database
value of 12.000
. In precision_mode
1
, placing the same Python integer in the same
field will result in a database value of 0.012
,
because precision_mode
1
places the
burden of scaling on the client programmer.
Note that in order to function correctly, code written for
KInterbasDB 2.x will require that each connection's
precision_mode
be explicitly set to 1
.
server_version
attribute (read-only)The version string of the database server to which this connection is connected.
For example, a connection to Firebird 1.0 on Windows has the
following server_version
:
WI-V6.2.794 Firebird 1.0
default_tpb
attributeThe transaction parameter buffer (TPB) that will be used by default for new transactions opened in the context of this connection.
TPBs are constructed by adding together
kinterbasdb.isc_tpb_*
bitmask constants (see the
Interbase API Guide for a definition of these constants' meanings).
By default, a connection's default_tpb
attribute is
equal to kinterbasdb.default_tpb
.
Changing a connection's default_tpb
will
affect all transactions subsequently started on that connection,
unless the programmer overrides the
default_tpb
by providing a
TPB to the connection's begin
method when he starts
a transaction explicitly.
execute_immediate
methodExecutes a statement without caching its prepared form. The statement must not be of a type that returns a result set.
Before this method is called, a transaction must have been explicitly
started with the connection's begin
method. In most cases
(especially cases in which the same statement--perhaps a parameterized
statement--is executed repeatedly), it is better to create a cursor
using the connection's cursor
method, then execute the statement
using one of the cursor's execute methods.
Arguments:
sql
-
string containing the SQL statement to execute.database_info
method
Wraps the Interbase C API function isc_database_info
.
For documentation, see the Interbase 6 API Guide section entitled
"Requesting information about an attachment" (page 51).
Note that this method is a very thin wrapper around
function isc_database_info
.
This method does not attempt to interpret
its results except with regard to whether they are a string or an
integer.
For example, requesting isc_info_user_names
with the
call
con.database_info(kinterbasdb.isc_info_user_names, 's')
will return a binary string
containing a raw succession of length-name pairs. A thicker wrapper
might interpret those raw results and return a Python tuple, but it
would need to handle a multitude of special cases in order to cover
all possible isc_info_*
items.
Arguments:
request
- one of the
kinterbasdb.isc_info_*
constants.
result_type
-
must be either
's'
if you expect a string result, or
'i'
if you expect an integer result.
Cursor
class
description
attribute
KInterbasDB makes absolutely no guarantees about
description
except
those required by the Python Database API Specification 2.0 (that
is, description
is
either None
or a sequence of 7-item sequences).
Therefore, client programmers should not rely on
description
being an instance of a particular class or
type.
KInterbasDB provides several named positional constants to be
used as indices into a given element of description
.
The contents of a description
element are defined by
the DB API spec; these constants are provided merely for
convenience.
DESCRIPTION_NAME DESCRIPTION_TYPE_CODE DESCRIPTION_DISPLAY_SIZE DESCRIPTION_INTERNAL_SIZE DESCRIPTION_PRECISION DESCRIPTION_SCALE DESCRIPTION_NULL_OK
Here is an example of accessing the name of the first
field in the description
of cursor cur
:
nameOfFirstField = cur.description[0][kinterbasdb.DESCRIPTION_NAME]
fetch*
methods
KInterbasDB makes absolutely no guarantees
about the return value of the
fetchone
/ fetchmany
/ fetchall
methods except that it is a sequence indexed by
field position.
KInterbasDB makes absolutely no guarantees
about the return value of the
fetchonemap
/ fetchmanymap
/ fetchallmap
methods (documented below)
except that it is a mapping of field name to field
value.
Therefore, client programmers should not rely on the return value being an instance of a particular class or type.
fetchonemap
method
This method is just like fetchone
, except that it returns a mapping
of field name to field value, rather than a sequence.
fetchmanymap
method
This method is just like fetchmany
, except that it returns a sequence
of mappings of field name to field value, rather than a sequence of
sequences.
fetchallmap
method
This method is just like fetchall
, except that it returns a sequence
of mappings of field name to field value, rather than a sequence of
sequences.
KInterbasDB does not support the following features of the Interbase/Firebird database engine:
ARRAY
datatype
Although KInterbasDB 3.0 lacks direct support for the Interbase/Firebird
ARRAY
datatype, it is still possible to create array
values by manually constructing their SQL string representations,
and to retrieve data stored in an array field by refering to specific
elements of the array using SQL.
Suppose, for example, that table tbl
has a 2-element array
field named array_field
. Instead of:
select array_field from tbl
one could use
select array_field[1] as element1, array_field[2] as element2 from tbl
Direct support for arrays is planned for KInterbasDB 3.1.
For more information about the database ARRAY
datatype, see
page 80 of the Interbase 6 Data Definition Guide or page 149 of the
Interbase 6 API Guide.
KInterbasDB 3.0 does not provide any way for the Python programmer to
listen for database events (which can be raised in stored procedures or
triggers with the POST_EVENT
statement). KInterbasDB 3.1
will definitely support events; in fact, a prototype is already
written (as of 2002.06.23).
For more information about database events, see page 187 of the
Interbase 6 API Guide.
KInterbasDB 3.0 does not wrap the Interbase Services API, but support is planned for KInterbasDB 3.1.
For more information about the Services API, see page 199 of the Interbase 6 API Guide.
This is not a comprehensive Python Database API tutorial, nor is it comprehensive in its coverage of anything else. It merely aims to demonstrate common KInterbasDB usage patterns, and to illustrate useful features of KInterbasDB that the Python Database API specification does not address.
A database connection is typically established with code such as this:
# The server is named 'stalin'; the database file is at # 'd:/code/projects/kinterbasdb/ibtest.db'. import kinterbasdb con = kinterbasdb.connect( dsn="stalin:d:/code/projects/kinterbasdb/ibtest.db", user="sysdba", password="pass" )
Suppose we want to connect to an Interbase 5.5 server, specifying UNICODE_FSS as the character set of the connection:
import kinterbasdb con = kinterbasdb.connect( dsn="stalin:d:/code/projects/kinterbasdb/ibtest.db", user="sysdba", password="pass", dialect=1, # necessary for Interbase < 6.0 charset="UNICODE_FSS" # specify a character set for the connection )
For this section, suppose we have a table defined and populated by the following SQL code:
create table people ( name_last varchar(20), age integer ); insert into people (name_last, age) values ('Yeltsin', 69); insert into people (name_last, age) values ('Gorbachev', 72);
This example shows the simplest way to
print the entire contents of the people
table:
import kinterbasdb con = kinterbasdb.connect( dsn="stalin:d:/code/projects/kinterbasdb/ibtest.db", user="sysdba", password="pass" ) cur = con.cursor() # Get a Cursor object that operates in the context of # Connection con. cur.execute("select * from people") # Execute the SELECT statement. print cur.fetchall() # Retrieve all rows as a sequence, then print that sequence.
Sample output:
[('Yeltsin', 69), ('Gorbachev', 72)]
Let's try a more meaningful example.
Suppose we want to print the contents of the
name_last
field for every row in the people
table:
import kinterbasdb con = kinterbasdb.connect( dsn="stalin:d:/code/projects/kinterbasdb/ibtest.db", user="sysdba", password="pass" ) cur = con.cursor() # Get a Cursor object that operates in the context of # Connection con. cur.execute("select name_last from people") # Execute the SELECT statement. print 'NAME_LAST' print '---------' while 1: # Loop until instructed otherwise. row = cur.fetchonemap() # Get a mapping of field name to field value # for the next available row. if row is None: # If we've reached the end of the record set, end the loop. break print row['name_last'] # Print the value of the name_last field in the # current row.
Sample output:
NAME_LAST --------- Yeltsin Gorbachev
The following program is a generic table printer
(applied in this example to people
):
import kinterbasdb, string TABLE_NAME = 'people' con = kinterbasdb.connect( dsn="stalin:d:/code/projects/kinterbasdb/ibtest.db", user="sysdba", password="pass" ) cur = con.cursor() cur.execute("select * from %s" % TABLE_NAME) # Print a header. for d in cur.description: print ( string.ljust( d[kinterbasdb.DESCRIPTION_NAME], d[kinterbasdb.DESCRIPTION_DISPLAY_SIZE] ) ), print # Finish the header row with a newline. print '-' * 78 # Print each row. while 1: row = cur.fetchone() if row is None: break # Print the value of each field left-justified within the maximum # possible width of that field. for fieldPos in range(len(row)): print ( string.ljust( str(row[fieldPos]), cur.description[fieldPos][kinterbasdb.DESCRIPTION_DISPLAY_SIZE] ) ), print # Finish the row with a newline.
Sample output:
NAME_LAST AGE ------------------------------------------------------------------------------ Yeltsin 69 Gorbachev 72
Let's insert more people into the people
table:
import kinterbasdb, string con = kinterbasdb.connect( dsn="stalin:d:/code/projects/kinterbasdb/ibtest.db", user="sysdba", password="pass" ) cur = con.cursor() newPeople = ( ('Lebed' , 56), ('Zhirinovsky' , 49) ) for newPerson in newPeople: cur.execute("insert into people (name_last, age) values (?, ?)", newPerson) con.commit() # The changes will not be saved unless the transaction is # committed explicitly.
Note the use of a parameterized SQL statement above. When dealing with repetitive statements, this is faster and less error-prone than assembling each SQL statement manually.
After running Example 4, the table printer from Example 3 would print:
NAME_LAST AGE ------------------------------------------------------------------------------ Yeltsin 69 Gorbachev 72 Lebed 56 Zhirinovsky 49
Interbase and Firebird support stored procedures written in a propriety procedural SQL language. IB/FB stored procedures can have input parameters and/or output parameters, but not combined input/output parameters.
It is important to distinguish between procedures that return a
result set and procedures that populate and return their
output parameters exactly once (conceptually, these latter "return their
output parameters").
IB/FB's server-side procedural SQL syntax
makes no such distinction, but client programmers must do so in
their SQL code.
A result set is retrieved from a stored procedure by
SELECT
ing from the procedure, whereas output
parameters are retrieved with an EXECUTE PROCEDURE
statement.
To retrieve a result set from a stored procedure with KInterbasDB, use code such as this:
cur.execute("select output1, output2 from the_proc(?, ?)", (input1, input2)) # Ordinary fetch code here, such as: result_set = cur.fetchall()
To call a stored procedure and access its output parameters with KInterbasDB, use code such as this:
cur.callproc("the_proc", (input1, input2)) # If there are output parameters, retrieve them as though they were the # first row of a result set. For example: output_params = cur.fetchone()
The Interbase engine requires that virtually every database operation
take place in the context of a transaction.
Even a typical SELECT
statement has an associated transaction.
For the sake of simplicity, KInterbasDB lets the Python programmer
ignore transaction management to the greatest extent allowed by the
Python Database API Specification 2.0. The specification says,
"if the database supports an auto-commit feature, this must be
initially off". At a minimum, therefore, it is necessary to call the
commit
method of the connection in order to persist any
changes made to the database. Transactions left uncommitted by the
programmer will be rollback
ed when the connection is
garbage collected.
KInterbasDB also supports explicit transaction management.
Connections have a default_tpb
(default transaction parameter
buffer) attribute that can be used to specify the
characteristics of all transactions subsequently started on the connection.
The programmer can also start a transaction explicitly using the
connection's
begin
method, which may optionally be passed a transaction
parameter buffer for that single transaction. If begin
indeed
receives a transaction parameter buffer, it will be used for that
particular transaction, in place of the
connection's default_tpb
.
For more information, see
the documentation of Connection.default_tpb
elsewhere in this
document or Chapter 5 of the Interbase 6 API Guide.
The following example establishes an unobtrusive transaction to be used for read-only access to the database:
import kinterbasdb con = kinterbasdb.connect( 'stalin:d:/code/projects/kinterbasdb/ibtest.db', 'sysdba', 'pass') tpb = kinterbasdb.isc_tpb_read \ + kinterbasdb.isc_tpb_read_committed \ + kinterbasdb.isc_tpb_rec_version con.begin(tpb) # Now read some data using a cursor...
database_info
Function
The kinterbasdb.database_info
function is a very thin wrapper
around the Interbase/Firebird C API function isc_database_info
(documented in the Interbase 6 API Guide section entitled
"Requesting information about an attachment" that begins on page 51).
Reference documentation for kinterbasdb.database_info
is
already provided in the
Extensions and Caveats section of
this document, so this tutorial only supplies an example program:
import kinterbasdb con = kinterbasdb.connect( 'stalin:d:/code/projects/kinterbasdb/ibtest.db', 'sysdba', 'pass') # Retrieving an integer info item is quite simple. bytesInUse = con.database_info(kinterbasdb.isc_info_current_memory, 'i') print 'The server is currently using %d bytes of memory.' % bytesInUse # Retrieving a string info item is somewhat more involved, because # the information is returned in a raw binary buffer that must # be parsed according to the rules defined in the Interbase 6 API # Guide section entitled "Requesting buffer items and result buffer # values" (page 51). # Often, the buffer contains a succession of length-string pairs # (one byte telling the length of s, followed by s itself). # Function kinterbasdb.raw_byte_to_int is provided to convert a raw # byte to a Python integer (see examples below). buf = con.database_info(kinterbasdb.isc_info_db_id, 's') # Parse the filename from the buffer. beginningOfFilename = 2 # The second byte in the buffer contains the size of the database filename # in bytes. lengthOfFilename = kinterbasdb.raw_byte_to_int(buf[1]) filename = buf[beginningOfFilename:beginningOfFilename + lengthOfFilename] # Parse the host name from the buffer. beginningOfHostName = (beginningOfFilename + lengthOfFilename) + 1 # The first byte after the end of the database filename contains the size # of the host name in bytes. lengthOfHostName = kinterbasdb.raw_byte_to_int(buf[beginningOfHostName - 1]) host = buf[beginningOfHostName:beginningOfHostName + lengthOfHostName] print ('We are connected to the database at %s on host %s.' % (filename, host))
Sample output:
The server is currently using 17614848 bytes of memory. We are connected to the database at D:\CODE\PROJECTS\KINTER~1\IBTEST.DB on host STALIN.