Qore oracle Module
3.3
|
Contents of this documentation:
The oracle module provides an Oracle driver to Qore's DBI system, allowing Qore programs to take access Oracle databases through the Qore's Datasource
, DatasourcePool
, and SQLStatement
classes.
This module is released under the LGPL 2.1 and is tagged as such in the module's header (meaning it can be loaded unconditionally regardless of how the Qore library was initialized).
Also included with the binary oracle module:
DBMS_APPLICATION_INFO
package.Example of creating an Oracle Datasource (note that db_encoding, host, and port are optional - using the hostname and port allows connections to be established without TNS, without these parameters TNS is used):
or
This driver supports the following DBI capabilities:
DBI_CAP_TRANSACTION_MANAGEMENT
DBI_CAP_STORED_PROCEDURES
DBI_CAP_CHARSET_SUPPORT
DBI_CAP_LOB_SUPPORT
DBI_CAP_BIND_BY_VALUE
DBI_CAP_BIND_BY_PLACEHOLDER
DBI_CAP_HAS_EXECRAW
DBI_CAP_HAS_STATEMENT
DBI_CAP_HAS_SELECT_ROW
DBI_CAP_HAS_NUMBER_SUPPORT
DBI_CAP_HAS_OPTION_SUPPORT
DBI_CAP_SERVER_TIME_ZONE
DBI_CAP_AUTORECONNECT
DBI_CAP_HAS_ARRAY_BIND
DBI_CAP_HAS_RESULTSET_OUTPUT
The Datasource::getServerVersion()
and Datasource::getClientVersion()
methods are implemented for this driver. Datasource::getServerVersion()
returns a string giving server information similar to the following:
The Datasource::getClientVersion()
returns a hash giving version information in the following keys: major
, minor
, update
, patch
, port_update
.
Note: There seems to be a bug in Oracle 9i and earlier in the streaming OCILobRead()
function, where the LOB buffer must be at least twice as big as the LOB data to be read. This bug does not affect versions of the Qore oracle module linked with Oracle 10g libraries or later.
ocilib
(http://orclib.sourceforge.net/), note that ocilib
was highly modified to be usable in this module, mostly due to the fact that we use a separate environment data structure for each connection to ensure maximum thread scalability - the Oracle docs say that all operations on an environment handle or any handle derived from an environment handle (i.e. statement handles, etc) must be either wrapped in a mutex (when initialized with OCI_NO_MUTEX
) or will be wrapped in a mutex by oracle (with OCI_THREADED
and without OCI_NO_MUTEX
).When compiled again Qore 0.8.6+ the oracle driver support the following DBI options on each connection:
"optimal-numbers"
: return NUMBER
types as an integer if possible, if not as an arbitrary-precision number"string-numbers"
: return NUMBER
types as strings (for backwards-compatibility)"numeric-numbers"
: return NUMBER
types as arbitrary-precision number values"timezone"
: accepts a string argument that can be either a region name (ex: "Europe/Prague"
) or a UTC offset (ex: "+01:00"
) to set the server's time zone rules; this is useful if connecting to a database server in a different time zone. If this option is not set then the server's time zone is assumed to be the same as the client's time zone; see Time Zone Support.Options can be set in the Datasource
or DatasourcePool
constructors as in the following examples:
Options are stored separately for each connection.
The number options ("optimal-numbers"
, "string-numbers"
, and "numeric-numbers"
) are all mutually-exclusive; setting one automatically disables the others. These options also ignore their arguments; the last one set takes effect (also note that setting it with any value sets the option, even False).
The default if no other option is explicitly set is "optimal-numbers"
. Note that this represents a change from previous versions where Oracle NUMBER
values were returned as strings in order to avoid the loss of information. To set the old behavior, set the "string-numbers"
option when creating the Datasource
or DatasourcePool
object.
When retrieving Oracle data, Oracle types are converted to Qore types as per Default Oracle to Qore Mappings.
Binding by value is supported for any statement executed through this driver; Oracle types are converted to Qore types as per Binding by Value.
Binding by placeholder is required to retrieve values from a procedure or function call. The oracle driver assumes that any placeholder values are string values unless a placeholder buffer specification is passed in the argument position corresponding to the placeholder specification in the string. For placeholder buffer specification values, see Binding by Placeholder.
Oracle TIMESTAMP
data supports time resolution to the microsecond, however Qore's date/time value only supports a millisecond resolution. Any Oracle TIMESTAMP
values are rounded to millisecond resolution when converted to Qore data types. See also Time Zone Support.
PL/SQL code and stored procedure and function execution is supported; the following is an example of a stored procedure call using bind by value and bind by placeholder (the v
characters represent the positions for binding the arguments following the SQL string by value, and the placeholder names are prefixed by a colon):
This will bind the "Customer Name"
and "Customer-ID"
strings by value (as per Binding by Value these will be bound with Oracle type SQLT_STRING
), and the output variables will be bound by placeholder (the first two will be bound as per Binding by Placeholder with buffers of Oracle type SQLT_INT
, and the last placeholder buffer will get the default buffer type of SQLT_STRING
), resulting in a hash giving the values of the output variables:
The driver now sets the server's time zone rules when the connection is established; this is taken from the current time zone settings of the calling Program object and can also be overridden/changed by setting the "timezone"
driver option (see Driver Options).
All date/time values bound by value are converted to the server's time zone before binding to ensure that date/time values are stored correctly in the server.
When selecting date/time values, the values returned are tagged with the server's time zone.
Note that the above rules are applied when the current Program's time zone settings are different than the connection's time zone settings at the time when the write operation (bind) read operation (select) is performed. This is meant to provide consistent support to connecting to a database server in a different time zone.
When columns are duplicated in select statements, the duplicated columns are renamed by the driver with an underscore and a numerix suffix (column_
#) as follows:
Would return a result as follows (assuming 1 row in each table with id = 1):
{id: 1, id_1: 1, id_2: 1}
Argument | OCI Type | Description |
Type::Binary | SQLT_BIN | For use with BLOB columns, for example. |
Type::String | SQLT_STR | For use with string data, VARCHAR , CHAR , CLOB columns, etc |
Type::Int | SQLT_INT or SQLT_STR | if the int > 32-bits = SQLT_STR , <= 32-bit int = SQLT_INT |
Type::Boolean | SQLT_INT | True is bound as 1, False as 0 |
Type::Float | SQLT_BDOUBLE | For use with FLOAT , BINARY_FLOAT , BINARY_DOUBLE columns, etc |
Type::Date | SQLT_TIMESTAMP | For use with DATE , TIMESTAMP , etc columns |
Wrapped Type::Hash and Type::List | SQLT_NTY | For use with Named Types (Objects) |
Argument | OCI Type | Description |
Type::Binary | SQLT_BIN | For retrieving RAW data up to 65531 bytes in size. |
SQL::BLOB | SQLT_BLOB | For retrieving BLOB data. The LOB handle is used to read the entire BLOB content into a binary object. |
SQL::CLOB | SQLT_CLOB | For retrieving CLOB data. The LOB handle is used to read the entire CLOB content into a Qore string. |
SQL::RESULTSET | SQLT_RSET | For retrieving a result set as a SQLStatement object |
SQL::VARCHAR | SQLT_STR | For retrieving character data (VARCHAR2 , etc). To specify a buffer size larger than 512 bytes, simply use the size in bytes as the argument. See CHAR and VARCHAR2 to Qore String |
Type::Int | SQLT_INT | For retrieving integer numeric data up to 32 bits (for larger numbers or for non-integer numbers use SQL::VARCHAR or Type::Float . |
Type::Float | SQLT_BDOUBLE | For retrieving data in 64-bit floating point format. |
Type::Date | SQLT_TIMESTAMP | For retrieving dates and times. |
Type::Hash | SQLT_RSET | For retrieving result sets from a stored procedure that returns a cursor. |
Wrapped Type::Hash and Type::List | SQLT_NTY | For use with Named Types (Objects) |
Oracle Column Type | Qore Type | Notes |
REAL , FLOAT , DOUBLE PRECISION , BINARY_FLOAT , BINARY_DOUBLE | Type::Float | direct conversion |
DATE | Type::Date | direct conversion |
TIMESTAMP | Type::Date (absolute) | resolution to the microsecond |
TIMESTAMP WITH TIME ZONE | Type::Date (absolute) | includes time zone information and has a resolution to the microsecond |
TIMESTAMP WITH LOCAL TIME ZONE | Type::Date (absolute) | includes time zone information and has a resolution to the microsecond |
INTERVAL YEAR TO MONTH | Type::Date (relative) | direct conversion to a relative date |
INTERVAL DAY TO SECOND | Type::Date (relative) | direct conversion to a relative date |
SMALLINT , INTEGER | Type::Int | direct conversion |
NUMBER , NUMERIC , DECIMAL | Type::String | conversion to a string to avoid loss of precision |
CLOB | Type::String | the LOB handle is used to read the entire CLOB content into a string |
RAW , LONG RAW | Type::Binary | direct conversion |
BLOB | Type::Binary | the LOB handle is used to read the entire BLOB content into a binary object |
CURSOR (result set) | Type::Hash | the result set is returned as a hash of lists |
The Oracle driver supports the DBI_CAP_HAS_ARRAY_BIND
capability, so it can find arrays of bind values to SQL for highly efficient SQL communication with the server; data for an arbitrary number of rows can be sent to the server in one command.
Array binding support differs depending on the data type; additionally it's possible to mix arrays of values and single values when binding; single values will be repeated in each row bound.
Note that when arrays are bound; each array must have the same data type in each element or the element can contain NOTHING or NULL, both of which are bound as NULL.
Array Binding Support
Qore Type | IN | OUT | Single |
string | Y | Y | Y |
int | Y | Y | Y |
number | Y | Y | Y |
bool | Y | Y | Y |
float | Y | N | Y |
date | Y | N | Y |
binary | Y | N | Y |
Other non-null types are not supported.
Data returned from DatasourcePool::select()
(and similar methods) use real string sizes:
Let's assume follwing data:
Data returned from PL/SQL code use different approach. Strings returned from this call are right-trimmed because OCI does not provide exact value size in this case by PL/SQL nature. The trimming is mandatory to avoid values with full allocated buffer (like strings with 512 spaces at the end).
Qore Oracle driver supports Oracle objects and collections (NTY in the following text).
Special initialization of the driver is mandatory to use NTY in Qore scripts:
This statement imports additional functions for NTY binding and fetching and therefore is required in Qore code that wants to use these functions - the automatic loading of DBI drivers on reference happens at run-time, therefore any references to the functions provided by this module can only be resolved at parse time if the module is explicitly required as above.
Function | Description |
Qore::Oracle::bindOracleObject() | Binds a Qore value as a Object type typename . Hash keys are object attributes |
Qore::Oracle::placeholderOracleObject() | Allows fetching Object type typename . The returned hash is a plain Qore hash with keys set as the object's attributes |
Qore::Oracle::bindOracleCollection() | Binds value as a Collection typename |
Qore::Oracle::placeholderOracleCollection() | Allows fetching a Collection typename . The returned list is a plain Qore list of values with the collection's type |
Type names (strings) are case insensitive.
Key names in value
hashes are case sensitive and should follow Oracle uppercase naming convention.
Keys which are not found in the keys-attribute mappings are silently ignored. If there is a missing key for any attribute, an exception is thrown.
Functions can be nested so there can be for example list (collection) of objects and vice versa:
Sample named types defined in the DB:
Example of binding NTY:
Example of fetching NTY:
CREATE
[ OR
REPLACE
] TYPE
command. It's a common misundrestanding that PL/SQL types created for example in package specification can be used as NTY too, however this is an incorrect assumption. PL/SQL types cannot be used directly with the Oracle OCI
library, however you can use custom wrappers or any other workarounds, of course.Oracle Advanced Queueing (AQ) is the Oracle database's queue management feature. AQ provides a message queuing infrastructure as integral part of the Oracle server engine. It provides an API for enqueing messages to database queues. These messages can later be dequeued for asynchronous processing. Oracle AQ also provides functionality to preserve, track, document, correlate, and query messages in queues.
Features:
Special initialization of the driver is mandatory to use AQ in Qore scripts:
Qore Oracle module contains these classes to handle AQ:
Class | Description |
Qore::Oracle::AQQueue | Main queue handler |
Qore::Oracle::AQMessage | Enhanced features for message |
Unfortunately there are some known bugs in the Oracle Module which cannot be fixed in the Qore driver right now. These are bugs in the underlying Oracle C Interface (OCI) library mostly.
ORA-21525
(attribute number or (collection element at index) s violated its constraints) if the object contains numbers with size constraint. An example:placeholderOracleObject()
used in the place where is the placeholderOracleCollection()
expected by Oracle.Some versions of 11.2 client software is unable to receive change notifications/OCI Callbacks or AQ notifications against some versions of 11.2 database.
Typically no errors result, and the observed behavior is that the notification simply does not occur.
A row will be observed in DBA_CHANGE_NOTIFICATION_REGS
, but then be removed, without notification occurring at the client.
Note that the behavior of this problem is consistent, and no notifications at all will be received. This bug does not apply for intermittent behaviors.
In the case of 11.2.0.1 client pointing to 11.2.0.2 database, the following error may occur:
Behavior may be noticed as a result of upgrading either client or database software to the affected versions, or after applying a CPU that contains the fix for unpublished Bug 10065474.
This behavior is a result of Oracle Bug 10065474, and is specific to 11.2 database.
The fix is primarily a client side fix, however there are some database side changes that may be required once the client side fix is applied, so the fix for Oracle Bug 10065474 can actually CAUSE this behavior, if the change is not applied at both client and server.
Solution: Apply patches or upgrade versions as applicable to obtain a working combination.
Workaround: A workaround can be used in most cases to bypass the authentication phase, which is where the problem behavior occurs. To do that, set the following event in the database:
Server version | Client version | |||
11.2.0.1.0 | 11.2.0.2.0 | 11.2.0.2.0+fix | 11.2.0.3.0 | |
---|---|---|---|---|
11.2.0.1.0 | works | fails | works | fails |
11.2.0.2.0 | fails | works | fails | fails |
11.2.0.2.0+fix | works | fails | works | works |
11.2.0.3.0 | fails | works | works |
Due to the way that Apple has changed dynamic library path handling in recent releases of Darwin/macOS, the Oracle module's rpath must be updated with the path to the Oracle dynamic libraries.
Therefore if you have moved/upgraded your Oracle libraries or are using a binary module build on another system, execute the following command to update the module's internal rpath:
install_name_tool -add_rpath ${ORACLE_LIB_DIR} ${MODULE_NAME}
for example:
sudo install_name_tool -add_rpath ${ORACLE_INSTANT_CLIENT} /usr/local/lib/qore-modules/oracle*.qmod
Note that this should not be necessary when building and installing on the same system, as the rpath used when linking is set automatically when the module is installed.
STMT EXEC DESCRIBE
DBI method for efficient describing of SQL statements without executing the statement itself and therefore avoiding a large performance penalty for this operation (issue 2773)RESULTSET
placeholder specification and the DBI_CAP_HAS_RESULTSET_OUTPUT
driver capability that allow result set output variables to be returned as SQLStatement objects (issue 1300)ORA-03113
and ORA-03114
were not treated as lost connections (issue 1963)DBI-SELECT-ROW-ERROR
exceptions were being raised as ORACLE-SELECT-ROW-ERROR
exceptions (issue 2542)ORA-01041
errors (issue 802)LONG
column values (issue 2609)DATE
values in selects with dates bound by value when the date is in the same time zone as the server's time zone but has a different DST offset (issue 2448)ORA-01406
errorORA-01461
errors when more than one binary value > 4000 bytes was bound (issue 875)OracleDatasourceBase
in OracleExtensions for forward-compatibility with future versions of QoreNew Features and Bug Fixes
New Features and Bug Fixes
Bug Fixes
New Features and Bug Fixes
"optimal-numbers"
: return NUMBER
values as an integer if possible, if not as an arbitrary-precision number"string-numbers"
: return NUMBER
values as strings (for backwards-compatibility)"numeric-numbers"
: return NUMBER
values as arbitrary-precision number values"timezone"
: accepts a string argument that can be either a region name (ex: "Europe/Prague"
) or a UTC offset (ex: "+01:00"
) to set the server's time zone rules; this is useful if connecting to a database server in a different time zone. If this option is not set then the server's time zone is assumed to be the same as the client's time zone; see Time Zone Support.NUMBER
values are retrieved as int
if possible, otherwise as a number
type is returned."string-numbers"
option to return NUMBER
values as strings