Subject Re: XSQLDA/XSQLVAR issues
Author Aleksey Karyakin
"Jim Starkey" <jas@...> wrote in message
news:42150419.2050306@......
>
> Aleksey Karyakin wrote:
>
>>Why not introduce an incapsulated diagnostic object that may be
>>provided to
>>function calls or associated with other (statement, attachment, etc)
>>objects? This approach has been used in APIs for a while and could
>>meet all
>>requirements and have no pitfalls, or am I missing something?
>>
>>
>>
> Sorry, I missed this message until I was cleaning out my message
box.
> It does meet all requirements and has no pitfalls. If credit is
due, it
> is all yours. Want some heat, too?

Yeah, why not. Please forward the credit it to SQL Access Group
(IBM/Microsoft/et al), since the API for ODBC/SQL CLI contains the
sample
error handling technics I referred to. It's completely described in
http://msdn.microsoft.com/library/en-us/odbc/htm/odbcdiagnostics.asp,
there
are the short essence (sorry for offtopic). I be

Each object (environment, connection, statement) in ODBC contains its
own
internal diagnostic object. The diagnostic object doesn't have a
handle
itself thus cannot be accessed directly, only by reference to the
handle of
containing "normal" object. Each and every API function (with the
exception
of the functions dealing the diagnostics itself and some other minor
exceptions) resets the diagnostic object on entry and populates it
with some
information related to current API call processing. As a result, API
functions on the same object are not reentrant (by definition of the
API).

The diagnostic information consists of:
-- overall result saying if the function completed or failed along
with a
few special codes (like SQL_NO_MORE_DATA for end of cursor), these
codes are
intended for program checks;
-- linear vector of messages. Each message contains SQL standard
status
code, native code, error message text. And there is a bunch of other
information not relevant here. Messages are primarily intended for
relaying
to a human, however, an application may analize error codes to look
for
expected errors like referential constraint violations.
-- other crap like number of rows affected by update, etc.

And there are two functions to read the disgnostics. The one is a
one-item-at-a-time function capable of retrieving any kind of
information
using generic binary buffer. The information is referred to using
Record
Number (the 1-based index of message or 0 for header fields) and
Field
Identifier (kind of data to retrieve).

The other function is shortcut for lazy programmers of the first,
retrieving
the most usable information at once. Given the record number, it gets
the
SQLSTATE, native error number and the text message.

So, returning back to Firebird, it's interesting if the similar
scheme can
be employed.

Personally I don't like the lack of reentrancy due to the
architecture
placement of the diagnostic information. But 90% in practice it
doesn't hurt
since every object has state anyway and therefore it's mostly
impossible to
call different functions at a time on a object. There is also a
potential
aspect of asynchronous calls, if Firebird ever have it. Due to its
ir-reentrancy (?), ODBC allows only one async call outstanding.
Separate
diagnosting object would help tracking the execution state of a call.

So, the proposed API could look like (just a sketch):

1. A diagnostic handle

typedef void* _handle; // or other opaque type
...
typedef _handle _diag_handle;

2. Functions to craete and destroy a diagnostic handle

_RESULT _allocate_diag(
_db_handle,
_diag_handle*);

_RESULT _free_diag(
_diag_handle*);

3. A function to read the diagnostics (ala isc_stmt_info() existing
API)

_get_diag(
_diag_handle,
unsigned information_items_len,
const octet* information_items,
unsinged result_buffer_len,
octet* result_buffer);

It is desired to process more information in one call so record
indexing
could be done using special codes in information buffer but not a
function
parameter.

Information items may include:

enum
{
_diag_result_code,
_diag_number_of_records,
_diag_start_record_number, // client request to continue using
the
provided record number
_diag_record, // indicates start of record fields
_diag_record_end, // indicates end of record fields
_diag_record_number,
_diag_sqlcode,
_diag_error_code,
_diag_message_text,
...
_diag_rows_inserted,
...
// etc, to be extended
};

4. Every API function returns a value to indicate completion and
takes the
diagnostic handle as an argument

typedef enum
{
_SUCCESS = 0,
_FAILURE,
// other values may be added:
// _ASYNC_EXECUTING
// _NO_MORE_ROWS
} _RESULT;

_RESULT _do_something_valuable(
_diag_handle,
....);

The diagnostic object is implemented by the underlying API level.
This is a pure C API, completely portable, doesn't rely on exception
handling or thread-local data and is object-oriented.

Regards,
Aleksey Karyakin


> --
>
> Jim Starkey
> Netfrastructure, Inc.
> 978 526-1376