8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 09:23:03 +01:00
firebird-mirror/src/dsql/user_dsql.cpp
2009-11-21 09:24:20 +00:00

1321 lines
32 KiB
C++

/*
* PROGRAM: Dynamic SQL runtime support
* MODULE: user_dsql.cpp
* DESCRIPTION: Above the Y-valve entrypoints for DSQL
*
*
* This module contains DSQL related routines that sit on top
* of the Y-valve. This includes the original (pre-version 4)
* DSQL routines as well as the alternate VMS and Ada entrypoints
* of the new DSQL routines. The pre-version 4 entrypoints
* retain their "gds__" prefix while the new routines are prefixed
* with "isc_dsql_".
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*
* 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "MPEXL" port
*
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
*
* 2003.01.12 Alex Peshkoff - Code cleanup, bad level of indirection
*
*/
#include "firebird.h"
#include "../jrd/common.h"
#include "../dsql/chars.h"
#include "../dsql/sqlda.h"
#include "../jrd/gds_proto.h"
#include "../jrd/why_proto.h"
#include "../dsql/user__proto.h"
#include "gen/iberror.h"
#include "../common/classes/init.h"
#include "../common/classes/rwlock.h"
#include "../common/StatusArg.h"
#include <stdio.h>
#include <stdlib.h>
using namespace Firebird;
enum name_type {
NAME_statement = 1,
NAME_cursor = 2
};
// declare a structure which enables us to associate a cursor with a
// statement and vice versa
struct dsql_dbb
{
dsql_dbb* dbb_next;
FB_API_HANDLE dbb_handle;
};
struct dsql_name; // fwd. decl.
struct dsql_stmt
{
dsql_stmt* stmt_next; // next in chain
dsql_name* stmt_stmt; // symbol table entry for statement name
dsql_name* stmt_cursor; // symbol table entry for cursor name
FB_API_HANDLE stmt_handle; // stmt handle returned by dsql_xxx
FB_API_HANDLE stmt_db_handle; // database handle for this statement
};
// declare a structure to hold the cursor and statement names
struct dsql_name
{
dsql_name* name_next;
dsql_name* name_prev;
dsql_stmt* name_stmt;
USHORT name_length;
SCHAR name_symbol[1];
};
// error block
struct dsql_err_stblock
{
ISC_STATUS* dsql_status;
ISC_STATUS* dsql_user_status;
};
static void cleanup(void*);
static void cleanup_database(FB_API_HANDLE*, void*);
static ISC_STATUS error(const Firebird::Exception& ex);
static ISC_STATUS error();
static void error_post(const Arg::StatusVector& v);
static dsql_name* lookup_name(const SCHAR*, dsql_name*);
static dsql_stmt* lookup_stmt(const SCHAR*, dsql_name*, name_type);
static void init(FB_API_HANDLE*);
static dsql_name* insert_name(const SCHAR*, dsql_name**, dsql_stmt*);
static USHORT name_length(const SCHAR*);
static void remove_name(dsql_name*, dsql_name**);
static bool scompare(const SCHAR*, USHORT, const SCHAR*, USHORT);
// declare the private data
static bool init_flag = false; // whether we've been initialized
static dsql_err_stblock* UDSQL_error = NULL;
static dsql_stmt* statements = NULL;
static dsql_name* statement_names = NULL;
static dsql_name* cursor_names = NULL;
static dsql_dbb* databases = NULL;
Firebird::GlobalPtr<Firebird::RWLock> global_sync;
static inline void set_global_private_status(ISC_STATUS* user_status, ISC_STATUS* local_status)
{
UDSQL_error->dsql_user_status = user_status;
UDSQL_error->dsql_status = user_status ? user_status : local_status;
}
static inline void INIT_DSQL(ISC_STATUS* user_status, ISC_STATUS* local_status)
{
init(0);
set_global_private_status(user_status, local_status);
}
//____________________________________________________________
//
// Close a dynamic SQL cursor.
//
ISC_STATUS API_ROUTINE isc_embed_dsql_close(ISC_STATUS* user_status, const SCHAR* name)
{
ISC_STATUS_ARRAY local_status;
INIT_DSQL(user_status, local_status);
try
{
// get the symbol table entry
dsql_stmt* statement = lookup_stmt(name, cursor_names, NAME_cursor);
return isc_dsql_free_statement(user_status, &statement->stmt_handle, DSQL_close);
}
catch (const Firebird::Exception& ex)
{
return error(ex);
}
}
//____________________________________________________________
//
// Declare a cursor for a dynamic request.
//
ISC_STATUS API_ROUTINE isc_embed_dsql_declare(ISC_STATUS* user_status,
const SCHAR* stmt_name,
const SCHAR* cursor)
{
ISC_STATUS_ARRAY local_status;
INIT_DSQL(user_status, local_status);
try
{
// get the symbol table entry
dsql_stmt* statement = lookup_stmt(stmt_name, statement_names, NAME_statement);
const ISC_STATUS s = isc_dsql_set_cursor_name(user_status, &statement->stmt_handle, cursor, 0);
if (s) {
return s;
}
Firebird::WriteLockGuard guard(global_sync);
statement->stmt_cursor = insert_name(cursor, &cursor_names, statement);
return s;
}
catch (const Firebird::Exception& ex)
{
return error(ex);
}
}
//____________________________________________________________
//
// Describe output parameters for a prepared statement.
//
ISC_STATUS API_ROUTINE isc_embed_dsql_describe(ISC_STATUS* user_status,
const SCHAR* stmt_name,
USHORT dialect, XSQLDA* sqlda)
{
ISC_STATUS_ARRAY local_status;
INIT_DSQL(user_status, local_status);
try
{
// get the symbol table entry
dsql_stmt* statement = lookup_stmt(stmt_name, statement_names, NAME_statement);
return isc_dsql_describe(user_status, &statement->stmt_handle, dialect, sqlda);
}
catch (const Firebird::Exception& ex)
{
return error(ex);
}
}
//____________________________________________________________
//
// isc_embed_dsql_descr_bind
//
ISC_STATUS API_ROUTINE isc_embed_dsql_descr_bind(ISC_STATUS* user_status,
const SCHAR* stmt_name,
USHORT dialect,
XSQLDA* sqlda)
{
return isc_embed_dsql_describe_bind(user_status, stmt_name, dialect, sqlda);
}
//____________________________________________________________
//
// Describe input parameters for a prepared statement.
//
ISC_STATUS API_ROUTINE isc_embed_dsql_describe_bind(ISC_STATUS* user_status,
const SCHAR* stmt_name,
USHORT dialect,
XSQLDA* sqlda)
{
ISC_STATUS_ARRAY local_status;
INIT_DSQL(user_status, local_status);
try
{
// get the symbol table entry
dsql_stmt* statement = lookup_stmt(stmt_name, statement_names, NAME_statement);
return isc_dsql_describe_bind(user_status, &statement->stmt_handle, dialect, sqlda);
}
catch (const Firebird::Exception& ex)
{
return error(ex);
}
}
//____________________________________________________________
//
// Execute a non-SELECT dynamic SQL statement.
//
ISC_STATUS API_ROUTINE isc_embed_dsql_execute(ISC_STATUS* user_status,
FB_API_HANDLE* trans_handle,
const SCHAR* stmt_name,
USHORT dialect,
XSQLDA* sqlda)
{
return isc_embed_dsql_execute2(user_status, trans_handle, stmt_name, dialect, sqlda, NULL);
}
//____________________________________________________________
//
// Execute a non-SELECT dynamic SQL statement.
//
ISC_STATUS API_ROUTINE isc_embed_dsql_execute2(ISC_STATUS* user_status,
FB_API_HANDLE* trans_handle,
const SCHAR* stmt_name,
USHORT dialect,
XSQLDA* in_sqlda,
XSQLDA* out_sqlda)
{
ISC_STATUS_ARRAY local_status;
INIT_DSQL(user_status, local_status);
try
{
// get the symbol table entry
dsql_stmt* statement = lookup_stmt(stmt_name, statement_names, NAME_statement);
return isc_dsql_execute2(user_status, trans_handle, &statement->stmt_handle,
dialect, in_sqlda, out_sqlda);
}
catch (const Firebird::Exception& ex)
{
return error(ex);
}
}
//____________________________________________________________
//
// isc_embed_dsql_exec_immed
//
ISC_STATUS API_ROUTINE isc_embed_dsql_exec_immed(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* trans_handle,
USHORT length,
const SCHAR* string,
USHORT dialect,
XSQLDA* sqlda)
{
return isc_embed_dsql_execute_immed(user_status, db_handle, trans_handle,
length, string, dialect, sqlda);
}
//____________________________________________________________
//
// Prepare a statement for execution.
//
ISC_STATUS API_ROUTINE isc_embed_dsql_execute_immed(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* trans_handle,
USHORT length,
const SCHAR* string,
USHORT dialect,
XSQLDA* sqlda)
{
return isc_embed_dsql_exec_immed2(user_status, db_handle, trans_handle,
length, string, dialect, sqlda, NULL);
}
//____________________________________________________________
//
// Prepare a statement for execution.
//
ISC_STATUS API_ROUTINE isc_embed_dsql_exec_immed2(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* trans_handle,
USHORT length,
const SCHAR* string,
USHORT dialect,
XSQLDA* in_sqlda,
XSQLDA* out_sqlda)
{
return isc_dsql_exec_immed2(user_status, db_handle, trans_handle,
length, string, dialect, in_sqlda, out_sqlda);
}
//____________________________________________________________
//
// Fetch next record from a dynamic SQL cursor
//
ISC_STATUS API_ROUTINE isc_embed_dsql_fetch(ISC_STATUS* user_status,
const SCHAR* cursor_name,
USHORT dialect, XSQLDA* sqlda)
{
ISC_STATUS_ARRAY local_status;
INIT_DSQL(user_status, local_status);
try
{
dsql_stmt* statement = lookup_stmt(cursor_name, cursor_names, NAME_cursor);
return isc_dsql_fetch(user_status, &statement->stmt_handle, dialect, sqlda);
}
catch (const Firebird::Exception& ex)
{
return error(ex);
}
}
//____________________________________________________________
//
// Fetch next record from a dynamic SQL cursor (ADA version)
//
ISC_STATUS API_ROUTINE isc_embed_dsql_fetch_a(ISC_STATUS* user_status,
int* sqlcode,
const SCHAR* cursor_name,
USHORT dialect,
XSQLDA* sqlda)
{
*sqlcode = 0;
ISC_STATUS s = isc_embed_dsql_fetch(user_status, cursor_name, dialect, sqlda);
if (s == 100) {
*sqlcode = 100;
}
return FB_SUCCESS;
}
ISC_STATUS API_ROUTINE isc_embed_dsql_insert(ISC_STATUS* user_status,
const SCHAR* cursor_name,
USHORT dialect, XSQLDA* sqlda)
{
/**************************************
*
* i s c _ e m b e d _ d s q l _ i n s e r t
*
**************************************
*
* Functional description
* Insert next record into a dynamic SQL cursor
*
**************************************/
ISC_STATUS_ARRAY local_status;
INIT_DSQL(user_status, local_status);
try
{
// get the symbol table entry
dsql_stmt* statement = lookup_stmt(cursor_name, cursor_names, NAME_cursor);
return isc_dsql_insert(user_status, &statement->stmt_handle, dialect, sqlda);
}
catch (const Firebird::Exception& ex)
{
return error(ex);
}
}
void API_ROUTINE isc_embed_dsql_length(const UCHAR* string, USHORT* length)
{
/**************************************
*
* i s c _ e m b e d _ d s q l _ l e n g t h
*
**************************************
*
* Functional description
* Determine length of a ';' or null terminated string
*
**************************************/
const UCHAR* p;
for (p = string; *p && *p != ';'; p++)
{
if (classes(*p) & CHR_QUOTE)
{
for (UCHAR prev = 0, quote = *p++; *p == quote || prev == quote;)
prev = *p++;
p--;
}
}
*length = p - string + 1;
}
ISC_STATUS API_ROUTINE isc_embed_dsql_open(ISC_STATUS* user_status,
FB_API_HANDLE* trans_handle,
const SCHAR* cursor_name,
USHORT dialect, XSQLDA* sqlda)
{
/**************************************
*
* i s c _ e m b e d _ d s q l _ o p e n
*
**************************************
*
* Functional description
* Open a dynamic SQL cursor.
*
**************************************/
return isc_embed_dsql_open2(user_status, trans_handle, cursor_name, dialect, sqlda, NULL);
}
ISC_STATUS API_ROUTINE isc_embed_dsql_open2(ISC_STATUS* user_status,
FB_API_HANDLE* trans_handle,
const SCHAR* cursor_name,
USHORT dialect,
XSQLDA* in_sqlda, XSQLDA* out_sqlda)
{
/**************************************
*
* i s c _ e m b e d _ d s q l _ o p e n 2
*
**************************************
*
* Functional description
* Open a dynamic SQL cursor.
*
**************************************/
ISC_STATUS_ARRAY local_status;
INIT_DSQL(user_status, local_status);
try
{
// get the symbol table entry
dsql_stmt* statement = lookup_stmt(cursor_name, cursor_names, NAME_cursor);
return isc_dsql_execute2(user_status, trans_handle, &statement->stmt_handle,
dialect, in_sqlda, out_sqlda);
}
catch (const Firebird::Exception& ex)
{
return error(ex);
}
}
ISC_STATUS API_ROUTINE isc_embed_dsql_prepare(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* trans_handle,
const SCHAR* stmt_name,
USHORT length,
const SCHAR* string,
USHORT dialect,
XSQLDA* sqlda)
{
/**************************************
*
* i s c _ e m b e d _ d s q l _ p r e p a r e
*
**************************************
*
* Functional description
* Prepare a statement for execution.
*
**************************************/
ISC_STATUS s;
ISC_STATUS_ARRAY local_status;
dsql_stmt* statement;
FB_API_HANDLE stmt_handle;
init(db_handle);
set_global_private_status(user_status, local_status);
try {
dsql_name* name = lookup_name(stmt_name, statement_names);
if (name && name->name_stmt->stmt_db_handle == *db_handle)
{
// The statement name already exists for this database.
// Re-use its statement handle.
statement = name->name_stmt;
stmt_handle = statement->stmt_handle;
}
else
{
// This is a new statement name for this database.
// Allocate a statement handle for it.
if (name) {
isc_embed_dsql_release(user_status, stmt_name);
}
statement = NULL;
stmt_handle = 0;
s = isc_dsql_allocate_statement(user_status, db_handle, &stmt_handle);
if (s)
{
return s;
}
}
s = isc_dsql_prepare(user_status, trans_handle, &stmt_handle, length, string, dialect, sqlda);
if (s)
{
// An error occurred. Free any newly allocated statement handle.
if (!statement)
{
ISC_STATUS_ARRAY local_status2;
isc_dsql_free_statement(local_status2, &stmt_handle, DSQL_drop);
}
return error();
}
// If a new statement was allocated, add it to the symbol table and insert it
// into the list of statements
Firebird::WriteLockGuard guard(global_sync);
if (!statement)
{
statement = (dsql_stmt*) gds__alloc((SLONG) sizeof(dsql_stmt));
// FREE: by user calling isc_embed_dsql_release()
if (!statement) // NOMEM:
error_post(Arg::Gds(isc_virmemexh));
#ifdef DEBUG_GDS_ALLOC
gds_alloc_flag_unfreed((void *) statement);
#endif // DEBUG_GDS_ALLOC
statement->stmt_next = statements;
statements = statement;
statement->stmt_db_handle = (FB_API_HANDLE) * db_handle;
statement->stmt_stmt = insert_name(stmt_name, &statement_names, statement);
}
else if (statement->stmt_cursor)
{
remove_name(statement->stmt_cursor, &cursor_names);
}
statement->stmt_handle = stmt_handle;
statement->stmt_cursor = NULL;
return s;
} // try
catch (const Firebird::Exception& ex)
{
return error(ex);
}
}
ISC_STATUS API_ROUTINE isc_embed_dsql_release(ISC_STATUS* user_status, const SCHAR* stmt_name)
{
/**************************************
*
* i s c _ e m b e d _ d s q l _ r e l e a s e
*
**************************************
*
* Functional description
* Release request for a dsql statement
*
**************************************/
ISC_STATUS_ARRAY local_status;
INIT_DSQL(user_status, local_status);
try
{
// If a request already exists under that name, purge it out
dsql_stmt* statement = lookup_stmt(stmt_name, statement_names, NAME_statement);
ISC_STATUS s = isc_dsql_free_statement(user_status, &statement->stmt_handle, DSQL_drop);
if (s) {
return s;
}
// remove the statement from the symbol tables
Firebird::WriteLockGuard guard(global_sync);
if (statement->stmt_stmt)
remove_name(statement->stmt_stmt, &statement_names);
if (statement->stmt_cursor)
remove_name(statement->stmt_cursor, &cursor_names);
// and remove this statement from the local list
dsql_stmt* p;
for (dsql_stmt** stmt_ptr = &statements; p = *stmt_ptr; stmt_ptr = &p->stmt_next)
{
if (p == statement)
{
*stmt_ptr = statement->stmt_next;
gds__free(statement);
break;
}
}
return s;
}
catch (const Firebird::Exception& ex)
{
return error(ex);
}
}
ISC_STATUS API_ROUTINE isc_dsql_fetch_a(ISC_STATUS* user_status,
int* sqlcode,
int* stmt_handle,
USHORT dialect, int* sqlda)
{
/**************************************
*
* i s c _ d s q l _ f e t c h _ a
*
**************************************
*
* Functional description
* Fetch next record from a dynamic SQL cursor (ADA version)
*
**************************************/
*sqlcode = 0;
const ISC_STATUS s = isc_dsql_fetch(user_status, reinterpret_cast<FB_API_HANDLE*>(stmt_handle),
dialect, reinterpret_cast<XSQLDA*>(sqlda));
if (s == 100)
*sqlcode = 100;
return FB_SUCCESS;
}
/**************************************
*
* i s c _ . . .
*
**************************************
*
* Functional description
* The following routines define the
* old isc_ entrypoints.
*
**************************************/
ISC_STATUS API_ROUTINE isc_close(ISC_STATUS* status_vector, const SCHAR* statement_name)
{
return isc_embed_dsql_close(status_vector, statement_name);
}
ISC_STATUS API_ROUTINE isc_declare(ISC_STATUS* status_vector,
const SCHAR* statement_name,
const SCHAR* cursor_name)
{
return isc_embed_dsql_declare(status_vector, statement_name, cursor_name);
}
ISC_STATUS API_ROUTINE isc_describe(ISC_STATUS* status_vector,
const SCHAR* statement_name, SQLDA* sqlda)
{
return isc_embed_dsql_describe(status_vector, statement_name,
DIALECT_sqlda, reinterpret_cast<XSQLDA*>(sqlda));
}
ISC_STATUS API_ROUTINE isc_describe_bind(ISC_STATUS* status_vector,
const SCHAR* statement_name,
SQLDA* sqlda)
{
return isc_embed_dsql_describe_bind(status_vector, statement_name,
DIALECT_sqlda, reinterpret_cast<XSQLDA*>(sqlda));
}
ISC_STATUS API_ROUTINE isc_dsql_finish(FB_API_HANDLE* /*db_handle*/)
{
return 0;
}
ISC_STATUS API_ROUTINE isc_execute(ISC_STATUS* status_vector, FB_API_HANDLE* tra_handle,
const SCHAR* statement_name, SQLDA* sqlda)
{
return isc_embed_dsql_execute(status_vector, tra_handle, statement_name,
DIALECT_sqlda, reinterpret_cast<XSQLDA*>(sqlda));
}
ISC_STATUS API_ROUTINE isc_execute_immediate(ISC_STATUS* status_vector,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
SSHORT* sql_length, const SCHAR* sql)
{
return isc_embed_dsql_execute_immed(status_vector, db_handle, tra_handle,
(USHORT) (sql_length ? *sql_length : 0),
sql, DIALECT_sqlda, NULL);
}
ISC_STATUS API_ROUTINE isc_fetch(ISC_STATUS* status_vector,
const SCHAR* cursor_name, SQLDA* sqlda)
{
return isc_embed_dsql_fetch(status_vector, cursor_name,
DIALECT_sqlda, reinterpret_cast<XSQLDA*>(sqlda));
}
ISC_STATUS API_ROUTINE isc_fetch_a(ISC_STATUS* status_vector,
int *sqlcode,
const SCHAR* cursor_name, SQLDA* sqlda)
{
return isc_embed_dsql_fetch_a(status_vector, sqlcode, cursor_name, DIALECT_sqlda,
reinterpret_cast<XSQLDA*>(sqlda));
}
ISC_STATUS API_ROUTINE isc_open(ISC_STATUS* status_vector,
FB_API_HANDLE* tra_handle,
const SCHAR* cursor_name, SQLDA* sqlda)
{
return isc_embed_dsql_open(status_vector, tra_handle, cursor_name, DIALECT_sqlda,
reinterpret_cast<XSQLDA*>(sqlda));
}
ISC_STATUS API_ROUTINE isc_prepare(ISC_STATUS* status_vector,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
const SCHAR* statement_name,
const SSHORT* sql_length,
const SCHAR* sql,
SQLDA* sqlda)
{
return isc_embed_dsql_prepare(status_vector, db_handle, tra_handle, statement_name,
(USHORT) (sql_length ? *sql_length : 0), sql,
DIALECT_sqlda, reinterpret_cast<XSQLDA*>(sqlda));
}
ISC_STATUS API_ROUTINE isc_dsql_release(ISC_STATUS* status_vector, const SCHAR* statement_name)
{
return isc_embed_dsql_release(status_vector, statement_name);
}
int API_ROUTINE isc_to_sqlda(SQLDA* , // sqlda
int , // number
SCHAR* , // host_var
int , // host_var_size
SCHAR* ) // name
{
// no longer supported
return 0;
}
//____________________________________________________________
//
// gds_...
//
// The following routines define the gds__ style names for isc_ entrypoints.
//
// Please note that these names - strictly speaking - are illegal C++
// identifiers, and as such should probably be moved into a C file.
//
ISC_STATUS API_ROUTINE gds__close(ISC_STATUS* status_vector, const SCHAR* statement_name)
{
return isc_close(status_vector, statement_name);
}
ISC_STATUS API_ROUTINE gds__declare(ISC_STATUS* status_vector,
const SCHAR* statement_name,
const SCHAR* cursor_name)
{
return isc_declare(status_vector, statement_name, cursor_name);
}
ISC_STATUS API_ROUTINE gds__describe(ISC_STATUS* status_vector,
const SCHAR* statement_name,
SQLDA* sqlda)
{
return isc_describe(status_vector, statement_name, sqlda);
}
ISC_STATUS API_ROUTINE gds__describe_bind(ISC_STATUS* status_vector,
const SCHAR* statement_name,
SQLDA* sqlda)
{
return isc_describe_bind(status_vector, statement_name, sqlda);
}
ISC_STATUS API_ROUTINE gds__dsql_finish(FB_API_HANDLE* db_handle)
{
return isc_dsql_finish(db_handle);
}
ISC_STATUS API_ROUTINE gds__execute(ISC_STATUS* status_vector,
FB_API_HANDLE* tra_handle,
const SCHAR* statement_name,
SQLDA* sqlda)
{
return isc_execute(status_vector, tra_handle, statement_name, sqlda);
}
ISC_STATUS API_ROUTINE gds__execute_immediate(ISC_STATUS* status_vector,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
SSHORT* sql_length,
const SCHAR* sql)
{
return isc_execute_immediate(status_vector, db_handle, tra_handle, sql_length, sql);
}
ISC_STATUS API_ROUTINE gds__fetch(ISC_STATUS* status_vector,
const SCHAR* statement_name,
SQLDA* sqlda)
{
return isc_fetch(status_vector, statement_name, sqlda);
}
ISC_STATUS API_ROUTINE gds__fetch_a(ISC_STATUS* status_vector,
int* sqlcode,
const SCHAR* statement_name,
SQLDA* sqlda)
{
return isc_fetch_a(status_vector, sqlcode, statement_name, sqlda);
}
ISC_STATUS API_ROUTINE gds__open(ISC_STATUS* status_vector,
FB_API_HANDLE* tra_handle,
const SCHAR* cursor_name,
SQLDA* sqlda)
{
return isc_open(status_vector, tra_handle, cursor_name, sqlda);
}
ISC_STATUS API_ROUTINE gds__prepare(ISC_STATUS* status_vector,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
const SCHAR* statement_name,
const SSHORT* sql_length,
const SCHAR* sql,
SQLDA* sqlda)
{
return isc_prepare(status_vector, db_handle, tra_handle, statement_name,
sql_length, sql, sqlda);
}
ISC_STATUS API_ROUTINE gds__to_sqlda(SQLDA* sqlda, int number,
SCHAR* host_variable, int host_variable_size,
SCHAR* host_variable_name)
{
return isc_to_sqlda(sqlda, number, host_variable, host_variable_size, host_variable_name);
}
//____________________________________________________________
//
// Helper functions for cleanup().
//
static void free_all_databases(dsql_dbb*& databasesL)
{
while (databasesL)
{
dsql_dbb* database = databasesL;
databasesL = database->dbb_next;
gds__free(database);
}
}
static void free_all_statements(dsql_stmt*& statementsL)
{
while (statementsL)
{
dsql_stmt* statement = statementsL;
statementsL = statement->stmt_next;
gds__free(statement);
}
}
static void free_all_names(dsql_name*& names)
{
while (names)
{
dsql_name* name = names;
names = name->name_next;
gds__free(name);
}
}
//____________________________________________________________
//
// Cleanup handler to free all dynamically allocated memory.
//
static void cleanup(void*)
{
if (!init_flag) {
return;
}
init_flag = false;
gds__free(UDSQL_error);
UDSQL_error = NULL;
{ // scope
Firebird::WriteLockGuard guard(global_sync);
free_all_databases(databases);
free_all_statements(statements);
free_all_names(statement_names);
free_all_names(cursor_names);
}
gds__unregister_cleanup(cleanup, 0);
}
//____________________________________________________________
//
// the cleanup handler called when a database is closed
//
static void cleanup_database(FB_API_HANDLE* db_handle, void* /*dummy*/)
{
if (!db_handle || !databases) {
return;
}
// for each of the statements in this database, remove it
// from the local list and from the hash table
Firebird::WriteLockGuard guard(global_sync);
dsql_stmt** stmt_ptr = &statements;
dsql_stmt* p;
while (p = *stmt_ptr)
{
if (p->stmt_db_handle == *db_handle)
{
*stmt_ptr = p->stmt_next;
if (p->stmt_stmt) {
remove_name(p->stmt_stmt, &statement_names);
}
if (p->stmt_cursor) {
remove_name(p->stmt_cursor, &cursor_names);
}
gds__free(p);
}
else
{
stmt_ptr = &p->stmt_next;
}
}
dsql_dbb* dbb;
for (dsql_dbb** dbb_ptr = &databases; dbb = *dbb_ptr; dbb_ptr = &dbb->dbb_next)
{
if (dbb->dbb_handle == *db_handle)
{
*dbb_ptr = dbb->dbb_next;
gds__free(dbb);
break;
}
}
}
//____________________________________________________________
//
// An error returned has been trapped. If the user specified
// a status vector, return a status code. Otherwise print the
// error code(s) and abort.
//
static ISC_STATUS error(const Firebird::Exception& ex)
{
if (UDSQL_error->dsql_user_status)
{
Firebird::stuff_exception(UDSQL_error->dsql_user_status, ex);
return UDSQL_error->dsql_user_status[1];
}
Firebird::stuff_exception(UDSQL_error->dsql_status, ex);
gds__print_status(UDSQL_error->dsql_status);
exit(UDSQL_error->dsql_status[1]);
return 0; // suppress compiler warning
}
static ISC_STATUS error()
{
if (UDSQL_error->dsql_user_status) {
return UDSQL_error->dsql_user_status[1];
}
gds__print_status(UDSQL_error->dsql_status);
exit(UDSQL_error->dsql_status[1]);
return 0; // suppress compiler warning
}
//____________________________________________________________
//
// Post an error sequence to the status vector. Since an error
// sequence can, in theory, be arbitrarily lock, pull a cheap
// trick to get the address of the argument vector.
//
// this is a copy of the routine found in err.c with the
// exception that it uses a different error block - one which
// is local to the V3 DSQL routines...
//
static void error_post(const Arg::StatusVector& v)
{
// Save status vector in appropriate place
v.copyTo(UDSQL_error->dsql_status);
// Give up whatever we were doing and return to the user.
Firebird::status_exception::raise(UDSQL_error->dsql_status);
}
//____________________________________________________________
//
// Initialize dynamic SQL. This is called only once.
//
static void init(FB_API_HANDLE* db_handle)
{
// If we haven't been initialized yet, do it now
if (!init_flag)
{
UDSQL_error = (dsql_err_stblock*) gds__alloc((SLONG) sizeof(dsql_err_stblock));
// FREE: by exit cleanup()
if (!UDSQL_error)
{
// NOMEM:
return; // Don't set the init_flag
}
init_flag = true;
gds__register_cleanup(cleanup, 0);
}
if (!db_handle) {
return;
}
dsql_dbb* dbb;
{ // scope
Firebird::ReadLockGuard guard(global_sync);
for (dbb = databases; dbb; dbb = dbb->dbb_next)
{
if (dbb->dbb_handle == *db_handle) {
return;
}
}
}
dbb = (dsql_dbb*) gds__alloc((SLONG) sizeof(dsql_dbb));
// FREE: by database exit handler cleanup_database()
if (!dbb)
{
// NOMEM
return; // Not a great error handler
}
Firebird::WriteLockGuard guard(global_sync);
dbb->dbb_next = databases;
databases = dbb;
dbb->dbb_handle = *db_handle;
ISC_STATUS_ARRAY local_status;
isc_database_cleanup(local_status, db_handle, cleanup_database, NULL);
}
static dsql_name* insert_name(const TEXT* symbol_name, dsql_name** list_ptr, dsql_stmt* stmt)
{
/**************************************
*
* i n s e r t _ n a m e
*
**************************************
*
* Functional description
* Add the name to the designated list.
*
**************************************/
const USHORT l = name_length(symbol_name);
dsql_name* name = (dsql_name*) gds__alloc((SLONG) sizeof(dsql_name) + l);
// FREE: by exit handler cleanup() or database_cleanup()
if (!name) // NOMEM:
error_post(Arg::Gds(isc_virmemexh));
name->name_stmt = stmt;
name->name_length = l;
memcpy(name->name_symbol, symbol_name, l);
if (name->name_next = *list_ptr)
name->name_next->name_prev = name;
*list_ptr = name;
name->name_prev = NULL;
return name;
}
static dsql_name* lookup_name(const TEXT* name, dsql_name* list)
{
/**************************************
*
* l o o k u p _ n a m e
*
**************************************
*
* Functional description
* Look up the given name in the designated list.
*
**************************************/
Firebird::ReadLockGuard guard(global_sync);
const USHORT l = name_length(name);
for (; list; list = list->name_next)
{
if (scompare(name, l, list->name_symbol, list->name_length)) {
break;
}
}
return list;
}
static dsql_stmt* lookup_stmt(const TEXT* name, dsql_name* list, name_type type)
{
/**************************************
*
* l o o k u p _ s t m t
*
**************************************
*
* Functional description
* Look up the given statement in the designated list.
*
**************************************/
dsql_name* found = lookup_name(name, list);
if (found)
return found->name_stmt;
if (type == NAME_statement)
{
error_post(Arg::Gds(isc_dsql_error) <<
Arg::Gds(isc_sqlerr) << Arg::Num(-518) <<
Arg::Gds(isc_dsql_request_err));
}
else
{
error_post(Arg::Gds(isc_dsql_error) <<
Arg::Gds(isc_sqlerr) << Arg::Num(-504) <<
Arg::Gds(isc_dsql_cursor_err) <<
Arg::Gds(isc_dsql_cursor_not_found) << Arg::Str(name));
}
return NULL;
}
static USHORT name_length(const TEXT* name)
{
/**************************************
*
* n a m e _ l e n g t h
*
**************************************
*
* Functional description
* Compute length of user supplied name.
*
**************************************/
// CVC: Beware, another function that stops at the first blank!!!
const TEXT* p = name;
while (*p && *p != ' ')
++p;
return (USHORT) (p - name);
}
static void remove_name(dsql_name* name, dsql_name** list_ptr)
{
/**************************************
*
* r e m o v e _ n a m e
*
**************************************
*
* Functional description
* Remove a name from the designated list.
*
**************************************/
if (name->name_next) {
name->name_next->name_prev = name->name_prev;
}
if (name->name_prev) {
name->name_prev->name_next = name->name_next;
}
else {
*list_ptr = name->name_next;
}
gds__free(name);
}
// CVC: It seems I've read at least three versions of this routine.
static bool scompare(const SCHAR* string1, USHORT length1, const SCHAR* string2, USHORT length2)
{
/**************************************
*
* s c o m p a r e
*
**************************************
*
* Functional description
* Compare two strings case insensitive according to the C locale rules.
*
**************************************/
if (length1 != length2) {
return false;
}
while (length1--)
{
const SCHAR c1 = *string1++;
const SCHAR c2 = *string2++;
if (c1 != c2 && UPPER7(c1) != UPPER7(c2))
{
return false;
}
}
return true;
}