8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-26 07:23:08 +01:00
firebird-mirror/src/jrd/why.cpp

6209 lines
142 KiB
C++

/*
* PROGRAM: JRD Access Method
* MODULE: why.cpp
* DESCRIPTION: Universal Y-valve
*
* 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): ______________________________________.
*
* 23-Feb-2002 Dmitry Yemanov - Events wildcarding
*
*
* 2002-02-23 Sean Leyne - Code Cleanup, removed old Win3.1 port (Windows_Only)
* 2002-02-23 Sean Leyne - Code Cleanup, removed old M88K and NCR3000 port
*
* 2002.10.27 Sean Leyne - Completed removal of obsolete "IMP" port
* 2002.10.27 Sean Leyne - Completed removal of obsolete "DG_X86" port
*
* 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "MPEXL" port
* 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "DecOSF" port
* 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "SGI" port
*
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
*
* 2002.10.30 Sean Leyne - Removed support for obsolete "PC_PLATFORM" define
*
* 2002.12.10 Alex Peshkoff: 1. Moved struct hndl declaration to h-file
* 2. renamed all specific objects to WHY_*
*
*/
#include "firebird.h"
#include "memory_routines.h" // needed for get_long
#include <stdlib.h>
#include <string.h>
#include "../jrd/common.h"
#include <stdarg.h>
#include <stdio.h>
#include "../jrd/gdsassert.h"
/* includes specific for DSQL */
#include "../dsql/sqlda.h"
/* end DSQL-specific includes */
#include "../jrd/why_proto.h"
#include "../jrd/y_handle.h"
#include "gen/iberror.h"
#include "../jrd/msg_encode.h"
#include "gen/msg_facs.h"
#include "../jrd/acl.h"
#include "../jrd/inf_pub.h"
#include "../jrd/thd.h"
#include "../jrd/isc.h"
#include "../jrd/fil.h"
#include "../jrd/flu.h"
#include "../jrd/db_alias.h"
#include "../jrd/os/path_utils.h"
#include "../common/classes/ClumpletWriter.h"
#include "../common/utils_proto.h"
#include "../jrd/flu_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/isc_proto.h"
#include "../jrd/isc_f_proto.h"
#ifndef REQUESTER
#include "../jrd/os/isc_i_proto.h"
#include "../jrd/isc_s_proto.h"
#include "../jrd/sch_proto.h"
#endif
#include "../jrd/thread_proto.h"
#include "../jrd/utl_proto.h"
#include "../dsql/dsql_proto.h"
#include "../dsql/prepa_proto.h"
#include "../dsql/utld_proto.h"
#include "../common/classes/rwlock.h"
#include "../common/classes/auto.h"
#include "../common/classes/init.h"
#include "../jrd/constants.h"
#if !defined (SUPERCLIENT) && !defined (REQUESTER) && !defined(SERVER_SHUTDOWN)
#define CANCEL_disable 1
#define CANCEL_enable 2
#define CANCEL_raise 3
//extern "C" ISC_STATUS jrd8_cancel_operation(ISC_STATUS *, Jrd::Attachment**, USHORT);
void JRD_process_close();
void JRD_database_close(Jrd::Attachment**, Jrd::Attachment**);
#endif
using namespace YValve;
// In 2.0 it's hard to include ibase.h in why.cpp due to API declaration conflicts.
// Taking into account that given y-valve lives it's last version,
// in which dpb version is not likely to change, define it here.
// #include "../jrd/ibase.h"
const UCHAR isc_dpb_version1 = 1;
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FLOCK
#include <sys/file.h> /* for flock() */
#endif
#ifdef WIN_NT
#include <windows.h>
#endif
#ifndef F_OK
#define F_OK 0
#endif
#ifndef F_TLOCK
#define F_TLOCK 2
#endif
const int IO_RETRY = 20;
inline void init_status(ISC_STATUS* vector)
{
vector[0] = isc_arg_gds;
vector[1] = FB_SUCCESS;
vector[2] = isc_arg_end;
}
inline bool is_network_error(const ISC_STATUS* vector)
{
return vector[1] == isc_network_error || vector[1] == isc_net_write_err ||
vector[1] == isc_net_read_err;
}
static void bad_handle(ISC_STATUS);
inline void nullCheck(const FB_API_HANDLE* ptr, ISC_STATUS code)
{
// this function is called for incoming API handlers,
// proposed to be created by the call
if (*ptr)
{
bad_handle(code);
}
}
//#define GET_STATUS { if (!(status = user_status)) status = local; init_status(status); }
// gone to YEntry
//#define RETURN_SUCCESS { subsystem_exit(); CHECK_STATUS_SUCCESS (status); return FB_SUCCESS; }
// gone to YEntry
#if defined(REQUESTER) || defined (SUPERCLIENT)
#define NO_LOCAL_DSQL
#endif
#if !defined (SUPERCLIENT) && !defined (REQUESTER)
static BOOLEAN shutdown_flag = FALSE;
#endif /* !SUPERCLIENT && !REQUESTER */
typedef ISC_STATUS(*PTR) (ISC_STATUS* user_status, ...);
/* Transaction element block */
struct teb
{
FB_API_HANDLE *teb_database;
int teb_tpb_length;
UCHAR *teb_tpb;
};
typedef teb TEB;
#ifdef DEBUG_GDS_ALLOC
#define alloc(x) alloc_debug(x, __FILE__, __LINE__)
static SCHAR *alloc_debug(SLONG, const char*, int);
#else
static SCHAR *alloc(SLONG);
#endif
static void free_block(void*);
namespace YValve
{
typedef Firebird::BePlusTree<Handle*, FB_API_HANDLE, MemoryPool, Handle> HandleMapping;
static Firebird::AutoPtr<HandleMapping> handleMapping;
static ULONG handle_sequence_number = 0;
static Firebird::RWLock handleMappingLock;
static Firebird::InitInstance<Firebird::SortedArray<Attachment*> > attachments;
Handle::Handle(UCHAR t, FB_API_HANDLE* pub, Attachment* par, USHORT imp)
: type(t), flags(0), implementation(par ? par->implementation : imp),
parent(par), user_handle(0)
{
fb_assert(par || (imp != USHORT(~0)));
handleMappingLock.beginWrite();
try
{
if (!handleMapping)
handleMapping = FB_NEW(*getDefaultMemoryPool())
HandleMapping(getDefaultMemoryPool());
// Loop until we find an empty handle slot.
// This is to care of case when counter rolls over
do {
// Generate handle number using rolling counter.
// This way we tend to give out unique handle numbers and closed
// handles do not appear as valid to our clients.
ULONG temp = ++handle_sequence_number;
// Avoid generating NULL handle when sequence number wraps
if (!temp)
temp = ++handle_sequence_number;
public_handle = (FB_API_HANDLE)(IPTR)temp;
} while (!handleMapping->add(this));
handleMappingLock.endWrite();
}
catch (const Firebird::Exception&) {
handleMappingLock.endWrite();
throw;
}
if (pub)
{
*pub = public_handle;
}
}
Handle* Handle::translate(FB_API_HANDLE handle) {
Firebird::ReadLockGuard sync(handleMappingLock);
if (handleMapping)
{
HandleMapping::Accessor accessor(handleMapping);
if (accessor.locate(handle))
{
Handle* h = accessor.current();
if (h->flags & HANDLE_shutdown)
{
Firebird::status_exception::raise(isc_shutdown, isc_arg_string,
h->parent ? h->parent->db_path.c_str() : "(unknown)",
isc_arg_end);
}
return h;
}
}
return 0;
}
Jrd::Attachment* Handle::getAttachmentHandle()
{
return parent ? parent->handle : 0;
}
void Handle::cancel()
{
if (!parent)
{
return;
}
parent->cancel2();
}
Handle::~Handle()
{
if (user_handle)
{
*user_handle = 0;
}
Firebird::WriteLockGuard sync(handleMappingLock);
// Silently ignore bad handles for PROD_BUILD
if (handleMapping && handleMapping->locate(public_handle))
{
handleMapping->fastRemove();
}
#ifdef DEV_BUILD
else
{
//Attempt to release bad handle
fb_assert(false);
}
#endif
}
Attachment::Attachment(StAtt* h, FB_API_HANDLE* pub, USHORT impl)
: Handle(hType(), pub, 0, impl),
transactions(*getDefaultMemoryPool()),
requests(*getDefaultMemoryPool()),
blobs(*getDefaultMemoryPool()),
statements(*getDefaultMemoryPool()),
handle(h),
db_path(*getDefaultMemoryPool()),
db_prepare_buffer(*getDefaultMemoryPool())
{
toParent<Attachment>(attachments(), this);
parent = this;
}
Attachment::~Attachment()
{
cleanup.call(&public_handle);
fromParent<Attachment>(attachments(), this);
}
}
#ifdef DEV_BUILD
static void check_status_vector(const ISC_STATUS*);
#endif
static void event_ast(void*, USHORT, const UCHAR*);
static void exit_handler(event_t*);
static Transaction* find_transaction(Attachment*, Transaction*);
inline Transaction* findTransaction(FB_API_HANDLE* public_handle, Attachment *a)
{
Transaction* t = find_transaction(a, translate<Transaction>(public_handle));
if (! t)
{
bad_handle(isc_bad_trans_handle);
}
return t;
}
static int get_database_info(ISC_STATUS *, Transaction*, TEXT **);
static const PTR get_entrypoint(int, int);
static USHORT sqlda_buffer_size(USHORT, XSQLDA*, USHORT);
static ISC_STATUS get_transaction_info(ISC_STATUS *, Transaction*, TEXT **);
static void iterative_sql_info(ISC_STATUS *, FB_API_HANDLE*, SSHORT, const SCHAR *, SSHORT,
SCHAR *, USHORT, XSQLDA *);
static ISC_STATUS open_blob(ISC_STATUS*, FB_API_HANDLE*, FB_API_HANDLE*, FB_API_HANDLE*, SLONG*,
USHORT, const UCHAR*, SSHORT, SSHORT);
static ISC_STATUS prepare(ISC_STATUS *, Transaction*);
static void release_dsql_support(sqlda_sup&);
static void save_error_string(ISC_STATUS *);
static bool set_path(const Firebird::PathName&, Firebird::PathName&);
static void subsystem_enter(void) throw();
static void subsystem_exit(void) throw();
#ifndef REQUESTER
static event_t why_event[1];
static SSHORT why_initialized = 0;
#endif
static SLONG why_enabled = 0;
/* subsystem_usage is used to count how many active ATTACHMENTs are
* running though the why valve. For the first active attachment
* request we reset the InterBase FPE handler.
* This counter is incremented for each ATTACH DATABASE, ATTACH SERVER,
* or CREATE DATABASE. This counter is decremented for each
* DETACH DATABASE, DETACH SERVER, or DROP DATABASE.
*
* A client-only API call, isc_reset_fpe() also controls the re-setting of
* the FPE handler.
* isc_reset_fpe (0); (default)
* Initialize the FPE handler the first time the gds
* library is made active.
* isc_reset_fpe (1);
* Initialize the FPE handler the NEXT time an API call is
* invoked.
* isc_reset_fpe (2);
* Revert to InterBase pre-V4.1.0 behavior, reset the FPE
* handler on every API call.
*/
static const USHORT FPE_RESET_INIT_ONLY = 0x0; /* Don't reset FPE after init */
static const USHORT FPE_RESET_NEXT_API_CALL = 0x1; /* Reset FPE on next gds call */
static const USHORT FPE_RESET_ALL_API_CALL = 0x2; /* Reset FPE on all gds call */
#if !(defined REQUESTER || defined SUPERCLIENT || defined SUPERSERVER)
extern ULONG isc_enter_count;
static ULONG subsystem_usage = 0;
static USHORT subsystem_FPE_reset = FPE_RESET_INIT_ONLY;
#define SUBSYSTEM_USAGE_INCR subsystem_usage++
#define SUBSYSTEM_USAGE_DECR subsystem_usage--
#else
#define SUBSYSTEM_USAGE_INCR /* nothing */
#define SUBSYSTEM_USAGE_DECR /* nothing */
#endif
/*
* Global array to store string from the status vector in
* save_error_string.
*/
const int MAXERRORSTRINGLENGTH = 250;
static TEXT glbstr1[MAXERRORSTRINGLENGTH];
static const TEXT glbunknown[10] = "<unknown>";
const USHORT PREPARE_BUFFER_SIZE = 32768; // size of buffer used in isc_dsql_prepare call
const USHORT DESCRIBE_BUFFER_SIZE = 1024; // size of buffer used in isc_dsql_describe_xxx call
namespace
{
/*
* class YEntry:
* 1. Provides correct status vector for operation and init() it.
* 2. Tracks subsystem_enter/exit() calls.
* For single-threaded systems:
* 3. Knows location of primary handle and detachs database when
* cancel / shutdown takes place.
* In some cases (primarily - attach/create) specific handle may
* be missing.
*/
class Status
{
public:
Status(ISC_STATUS* v) throw()
: local_vector(v ? v : local_status), doExit(true)
{
init_status(local_vector);
}
operator ISC_STATUS*() const
{
return local_vector;
}
// don't exit on missing user_status
// That feature is suspicious: on windows after
// printf() and exit() will happen silent exit. AP-2007.
void ok()
{
doExit = false;
}
~Status()
{
#ifdef DEV_BUILD
check_status_vector(local_vector);
#endif
#ifndef SUPERSERVER
if (local_vector == local_status &&
local_vector[0] == isc_arg_gds &&
local_vector[1] != FB_SUCCESS &&
doExit)
{
// user did not specify status, but error took place:
// should better specify it next time :-(
gds__print_status(local_vector);
exit((int) local_vector[1]);
}
#endif
}
private:
ISC_STATUS_ARRAY local_status;
ISC_STATUS* local_vector;
bool doExit;
};
#ifndef SERVER_SHUTDOWN // appears this macro has now nothing with shutdown
int totalAttachmentCount() const
{
return attachments().getCount();
}
template <typename Array>
void markHandlesShutdown(Array handles)
{
for (size_t n = 0; n < handles.getCount(); ++n)
{
handles[n]->flags |= HANDLE_shutdown;
}
}
void markShutdown(Attachment* attach)
{
attach->flags |= HANDLE_shutdown;
markHandlesShutdown(attach->transactions);
markHandlesShutdown(attach->statements);
markHandlesShutdown(attach->requests);
markHandlesShutdown(attach->blobs);
}
void markShutdown(Jrd::Attachment** list)
{
while (Jrd::Attachment* ja = *list++)
{
for (size_t n = 0; n < attachments().getCount(); ++n)
{
if (attachments()[n]->handle == ja)
{
markShutdown(attachments()[n]);
break;
}
}
}
}
class YEntry : public Status
{
public:
YEntry(ISC_STATUS* v) throw()
: Status(v), recursive(false)
{
subsystem_enter();
if (handle || killed) {
recursive = true;
return;
}
handle = 0;
vector = (ISC_STATUS*)(*this);
inside = true;
if (!init)
{
init = true;
installCtrlCHandler();
}
}
void setPrimaryHandle(Handle* h)
{
handle = h;
}
~YEntry()
{
subsystem_exit();
if (recursive)
{
return;
}
if (killed)
{
#if !defined (SUPERCLIENT) && !defined (REQUESTER)
JRD_process_close();
#endif
propagateKill();
}
if (fatal())
{
if (handle)
{
Jrd::Attachment* attach = handle->getAttachmentHandle();
Firebird::HalfStaticArray<Jrd::Attachment*, 2> releasedBuffer;
Jrd::Attachment** released =
releasedBuffer.getBuffer(totalAttachmentCount() + 1);
*released = 0;
#if !defined (SUPERCLIENT) && !defined (REQUESTER)
JRD_database_close(&attach, released);
#endif
markShutdown(released);
}
}
inside = false;
handle = 0;
}
private:
YEntry(const YEntry&); // prohibit copy constructor
bool recursive; // loopback call from ExecState, Udf, etc.
static bool inside;
static Handle* handle;
static ISC_STATUS* vector;
static bool init;
static int killed;
static bool proc2, proc15;
static void installCtrlCHandler() throw()
{
try
{
proc2 = ISC_signal(2, Handler2, 0);
proc15 = ISC_signal(15, Handler15, 0);
}
catch (...)
{
gds__log("Failure setting ctrl-C handlers");
}
}
static void propagateKill()
{
ISC_signal_cancel(2, Handler2, 0);
ISC_signal_cancel(15, Handler15, 0);
// if signal is not processed by someone else, exit now
if (!(killed == 2 ? proc2 : proc15))
{
exit(0);
}
// Someone else watches signals - let him shutdown gracefully
for (size_t n = 0; n < attachments().getCount(); ++n)
{
markShutdown(attachments()[n]);
}
}
static void Handler2(void*)
{
if (killed)
{
return; // do nothing - already killed
}
killed = 2;
Handler();
}
static void Handler15(void*)
{
if (killed)
{
return; // do nothing - already killed
}
killed = 15;
Handler();
}
static void Handler()
{
#if !defined (SUPERCLIENT) && !defined (REQUESTER)
shutdown_flag = true;
#endif
if (inside)
{
if (handle)
{
handle->cancel();
}
}
else
{
// this function must in theory use only signal-safe code
// but as long as we have not entered engine,
// any call to it should be safe
#if !defined (SUPERCLIENT) && !defined (REQUESTER)
JRD_process_close();
#endif
propagateKill();
}
}
bool fatal() const
{
return vector[0] == isc_arg_gds &&
(vector[1] == isc_shutdown ||
vector[1] == isc_cancelled);
}
};
bool YEntry::init = false;
bool YEntry::inside = false;
Handle* YEntry::handle = 0;
ISC_STATUS* YEntry::vector = 0;
int YEntry::killed = 0;
bool YEntry::proc2 = false;
bool YEntry::proc15 = false;
#else
class YEntry : public Status
{
public:
YEntry(ISC_STATUS* v) : Status(v)
{
subsystem_enter();
}
void setPrimaryHandle(Handle* h)
{
}
~YEntry()
{
subsystem_exit();
}
private:
YEntry(const YEntry&); //prohibit copy constructor
};
#endif
} // anonymous namespace
#ifdef VMS
#define CALL(proc, handle) (*get_entrypoint(proc, handle))
#else
#define CALL(proc, handle) (get_entrypoint(proc, handle))
#endif
#define GDS_ATTACH_DATABASE isc_attach_database
#define GDS_BLOB_INFO isc_blob_info
#define GDS_CANCEL_BLOB isc_cancel_blob
#define GDS_CANCEL_EVENTS isc_cancel_events
#define GDS_CANCEL_OPERATION gds__cancel_operation
#define GDS_CLOSE_BLOB isc_close_blob
#define GDS_COMMIT isc_commit_transaction
#define GDS_COMMIT_RETAINING isc_commit_retaining
#define GDS_COMPILE isc_compile_request
#define GDS_COMPILE2 isc_compile_request2
#define GDS_CREATE_BLOB isc_create_blob
#define GDS_CREATE_BLOB2 isc_create_blob2
#define GDS_CREATE_DATABASE isc_create_database
#define GDS_DATABASE_INFO isc_database_info
#define GDS_DDL isc_ddl
#define GDS_DETACH isc_detach_database
#define GDS_DROP_DATABASE isc_drop_database
//#define GDS_EVENT_WAIT gds__event_wait
#define GDS_INTL_FUNCTION gds__intl_function
#define GDS_DSQL_CACHE gds__dsql_cache
#define GDS_INTERNAL_COMPILE gds__internal_compile_request
#define GDS_GET_SEGMENT isc_get_segment
#define GDS_GET_SLICE isc_get_slice
#define GDS_OPEN_BLOB isc_open_blob
#define GDS_OPEN_BLOB2 isc_open_blob2
#define GDS_PREPARE isc_prepare_transaction
#define GDS_PREPARE2 isc_prepare_transaction2
#define GDS_PUT_SEGMENT isc_put_segment
#define GDS_PUT_SLICE isc_put_slice
#define GDS_QUE_EVENTS isc_que_events
#define GDS_RECONNECT isc_reconnect_transaction
#define GDS_RECEIVE isc_receive
#ifdef SCROLLABLE_CURSORS
#define GDS_RECEIVE2 isc_receive2
#endif
#define GDS_RELEASE_REQUEST isc_release_request
#define GDS_REQUEST_INFO isc_request_info
#define GDS_ROLLBACK isc_rollback_transaction
#define GDS_ROLLBACK_RETAINING isc_rollback_retaining
#define GDS_SEEK_BLOB isc_seek_blob
#define GDS_SEND isc_send
#define GDS_START_AND_SEND isc_start_and_send
#define GDS_START isc_start_request
#define GDS_START_MULTIPLE isc_start_multiple
#define GDS_START_TRANSACTION isc_start_transaction
#define GDS_TRANSACT_REQUEST isc_transact_request
#define GDS_TRANSACTION_INFO isc_transaction_info
#define GDS_UNWIND isc_unwind_request
#define GDS_DSQL_ALLOCATE isc_dsql_allocate_statement
#define GDS_DSQL_ALLOC isc_dsql_alloc_statement
#define GDS_DSQL_ALLOC2 isc_dsql_alloc_statement2
#define GDS_DSQL_EXECUTE isc_dsql_execute
#define GDS_DSQL_EXECUTE2 isc_dsql_execute2
#define GDS_DSQL_EXECUTE_M isc_dsql_execute_m
#define GDS_DSQL_EXECUTE2_M isc_dsql_execute2_m
#define GDS_DSQL_EXECUTE_IMMED isc_dsql_execute_immediate
#define GDS_DSQL_EXECUTE_IMM_M isc_dsql_execute_immediate_m
#define GDS_DSQL_EXEC_IMMED isc_dsql_exec_immediate
#define GDS_DSQL_EXEC_IMMED2 isc_dsql_exec_immed2
#define GDS_DSQL_EXEC_IMM_M isc_dsql_exec_immediate_m
#define GDS_DSQL_EXEC_IMM2_M isc_dsql_exec_immed2_m
#define GDS_DSQL_EXEC_IMM3_M isc_dsql_exec_immed3_m
#define GDS_DSQL_FETCH isc_dsql_fetch
#define GDS_DSQL_FETCH2 isc_dsql_fetch2
#define GDS_DSQL_FETCH_M isc_dsql_fetch_m
#define GDS_DSQL_FETCH2_M isc_dsql_fetch2_m
#define GDS_DSQL_FREE isc_dsql_free_statement
#define GDS_DSQL_INSERT isc_dsql_insert
#define GDS_DSQL_INSERT_M isc_dsql_insert_m
#define GDS_DSQL_PREPARE isc_dsql_prepare
#define GDS_DSQL_PREPARE_M isc_dsql_prepare_m
#define GDS_DSQL_SET_CURSOR isc_dsql_set_cursor_name
#define GDS_DSQL_SQL_INFO isc_dsql_sql_info
#define GDS_SERVICE_ATTACH isc_service_attach
#define GDS_SERVICE_DETACH isc_service_detach
#define GDS_SERVICE_QUERY isc_service_query
#define GDS_SERVICE_START isc_service_start
/*****************************************************
* IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT
*
* The order in which these defines appear MUST match
* the order in which the entrypoint appears in
* source/jrd/entry.h. Failure to do so will result in
* much frustration
******************************************************/
const int PROC_ATTACH_DATABASE = 0;
const int PROC_BLOB_INFO = 1;
const int PROC_CANCEL_BLOB = 2;
const int PROC_CLOSE_BLOB = 3;
const int PROC_COMMIT = 4;
const int PROC_COMPILE = 5;
const int PROC_CREATE_BLOB = 6;
const int PROC_CREATE_DATABASE = 7;
const int PROC_DATABASE_INFO = 8;
const int PROC_DETACH = 9;
const int PROC_GET_SEGMENT = 10;
const int PROC_OPEN_BLOB = 11;
const int PROC_PREPARE = 12;
const int PROC_PUT_SEGMENT = 13;
const int PROC_RECONNECT = 14;
const int PROC_RECEIVE = 15;
const int PROC_RELEASE_REQUEST = 16;
const int PROC_REQUEST_INFO = 17;
const int PROC_ROLLBACK = 18;
const int PROC_SEND = 19;
const int PROC_START_AND_SEND = 20;
const int PROC_START = 21;
const int PROC_START_MULTIPLE = 22;
const int PROC_START_TRANSACTION= 23;
const int PROC_TRANSACTION_INFO = 24;
const int PROC_UNWIND = 25;
const int PROC_COMMIT_RETAINING = 26;
const int PROC_QUE_EVENTS = 27;
const int PROC_CANCEL_EVENTS = 28;
const int PROC_DDL = 29;
const int PROC_OPEN_BLOB2 = 30;
const int PROC_CREATE_BLOB2 = 31;
const int PROC_GET_SLICE = 32;
const int PROC_PUT_SLICE = 33;
const int PROC_SEEK_BLOB = 34;
const int PROC_TRANSACT_REQUEST = 35;
const int PROC_DROP_DATABASE = 36;
const int PROC_DSQL_ALLOCATE = 37;
const int PROC_DSQL_EXECUTE = 38;
const int PROC_DSQL_EXECUTE2 = 39;
const int PROC_DSQL_EXEC_IMMED = 40;
const int PROC_DSQL_EXEC_IMMED2 = 41;
const int PROC_DSQL_FETCH = 42;
const int PROC_DSQL_FREE = 43;
const int PROC_DSQL_INSERT = 44;
const int PROC_DSQL_PREPARE = 45;
const int PROC_DSQL_SET_CURSOR = 46;
const int PROC_DSQL_SQL_INFO = 47;
const int PROC_SERVICE_ATTACH = 48;
const int PROC_SERVICE_DETACH = 49;
const int PROC_SERVICE_QUERY = 50;
const int PROC_SERVICE_START = 51;
const int PROC_ROLLBACK_RETAINING = 52;
const int PROC_CANCEL_OPERATION = 53;
const int PROC_INTL_FUNCTION = 54; // internal call
const int PROC_DSQL_CACHE = 55; // internal call
const int PROC_INTERNAL_COMPILE = 56; // internal call
const int PROC_count = 57;
struct ENTRY
{
const TEXT* name;
PTR address;
};
struct IMAGE
{
const TEXT* name;
TEXT path[MAXPATHLEN]; // avoid problems with code changing literals.
};
/* Define complicated table for multi-subsystem world */
extern "C" {
static ISC_STATUS no_entrypoint(ISC_STATUS * user_status, ...);
#ifdef VMS
#define RDB
#endif
#ifdef SUPERCLIENT
#define ENTRYPOINT(gen,cur,bridge,rem,os2_rem,csi,rdb,pipe,bridge_pipe,win,winipi) ISC_STATUS rem(ISC_STATUS * user_status, ...);
#else
#define ENTRYPOINT(gen,cur,bridge,rem,os2_rem,csi,rdb,pipe,bridge_pipe,win,winipi) ISC_STATUS rem(ISC_STATUS * user_status, ...), cur(ISC_STATUS * user_status, ...);
#endif
#include "../jrd/entry.h"
#ifdef RDB
#define ENTRYPOINT(gen,cur,bridge,rem,os2_rem,csi,rdb,pipe,bridge_pipe,win,winipi) ISC_STATUS rdb(ISC_STATUS * user_status, ...);
#include "../jrd/entry.h"
#endif
static const IMAGE images[] =
{
{"REMINT", "REMINT"}, /* Remote */
# if !defined(REQUESTER) && !defined(SUPERCLIENT)
{"GDSSHR", "GDSSHR"}, /* Primary access method */
#endif
#ifdef RDB
{"GDSRDB", "GDSRDB"}, /* Rdb Interface */
#endif
};
#define SUBSYSTEMS sizeof (images) / (sizeof (IMAGE))
static ENTRY entrypoints[PROC_count * SUBSYSTEMS] =
{
#define ENTRYPOINT(gen,cur,bridge,rem,os2_rem,csi,rdb,pipe,bridge_pipe,win,winipi) {NULL, rem},
#include "../jrd/entry.h"
# if !defined(REQUESTER) && !defined(SUPERCLIENT)
#define ENTRYPOINT(gen,cur,bridge,rem,os2_rem,csi,rdb,pipe,bridge_pipe,win,winipi) {NULL, cur},
#include "../jrd/entry.h"
#endif
#ifdef RDB
#define ENTRYPOINT(gen,cur,bridge,rem,os2_rem,csi,rdb,pipe,bridge_pipe,win,winipi) {NULL, rdb},
#include "../jrd/entry.h"
#endif
};
#ifndef SUPERCLIENT
static const TEXT *generic[] = {
#define ENTRYPOINT(gen,cur,bridge,rem,os2_rem,csi,rdb,pipe,bridge_pipe,win,winipi) gen,
#include "../jrd/entry.h"
};
#endif
} // extern "C"
/* Information items for two phase commit */
static const UCHAR prepare_tr_info[] =
{
isc_info_tra_id,
isc_info_end
};
/* Information items for DSQL prepare */
static const SCHAR sql_prepare_info[] =
{
isc_info_sql_select,
isc_info_sql_describe_vars,
isc_info_sql_sqlda_seq,
isc_info_sql_type,
isc_info_sql_sub_type,
isc_info_sql_scale,
isc_info_sql_length,
isc_info_sql_field,
isc_info_sql_relation,
isc_info_sql_owner,
isc_info_sql_alias,
isc_info_sql_describe_end
};
/* Information items for SQL info */
static const SCHAR describe_select_info[] =
{
isc_info_sql_select,
isc_info_sql_describe_vars,
isc_info_sql_sqlda_seq,
isc_info_sql_type,
isc_info_sql_sub_type,
isc_info_sql_scale,
isc_info_sql_length,
isc_info_sql_field,
isc_info_sql_relation,
isc_info_sql_owner,
isc_info_sql_alias,
isc_info_sql_describe_end
};
static const SCHAR describe_bind_info[] =
{
isc_info_sql_bind,
isc_info_sql_describe_vars,
isc_info_sql_sqlda_seq,
isc_info_sql_type,
isc_info_sql_sub_type,
isc_info_sql_scale,
isc_info_sql_length,
isc_info_sql_field,
isc_info_sql_relation,
isc_info_sql_owner,
isc_info_sql_alias,
isc_info_sql_describe_end
};
static const SCHAR sql_prepare_info2[] =
{
isc_info_sql_stmt_type,
// describe_select_info
isc_info_sql_select,
isc_info_sql_describe_vars,
isc_info_sql_sqlda_seq,
isc_info_sql_type,
isc_info_sql_sub_type,
isc_info_sql_scale,
isc_info_sql_length,
isc_info_sql_field,
isc_info_sql_relation,
isc_info_sql_owner,
isc_info_sql_alias,
isc_info_sql_describe_end,
// describe_bind_info
isc_info_sql_bind,
isc_info_sql_describe_vars,
isc_info_sql_sqlda_seq,
isc_info_sql_type,
isc_info_sql_sub_type,
isc_info_sql_scale,
isc_info_sql_length,
isc_info_sql_field,
isc_info_sql_relation,
isc_info_sql_owner,
isc_info_sql_alias,
isc_info_sql_describe_end
};
namespace YValve
{
void Attachment::cancel2()
{
#if !defined (SUPERCLIENT) && !defined (REQUESTER) && !defined(SERVER_SHUTDOWN)
ISC_STATUS_ARRAY local;
jrd8_cancel_operation(local, &handle, CANCEL_raise);
#endif
}
}
ISC_STATUS API_ROUTINE GDS_ATTACH_DATABASE(ISC_STATUS* user_status,
SSHORT file_length,
const TEXT* file_name,
FB_API_HANDLE* public_handle,
SSHORT dpb_length,
const SCHAR* dpb)
{
/**************************************
*
* g d s _ $ a t t a c h _ d a t a b a s e
*
**************************************
*
* Functional description
* Attach a database through the first subsystem
* that recognizes it.
*
**************************************/
ISC_STATUS *ptr;
ISC_STATUS_ARRAY temp;
StAtt* handle = 0;
Attachment* database = 0;
USHORT n;
YEntry status(user_status);
try
{
nullCheck(public_handle, isc_bad_db_handle);
if (!file_name)
{
Firebird::status_exception::raise(isc_bad_db_format,
isc_arg_string,
"",
isc_arg_end);
}
if (dpb_length > 0 && !dpb)
{
Firebird::status_exception::raise(isc_bad_dpb_form,
isc_arg_end);
}
#if !defined (SUPERCLIENT) && !defined (REQUESTER)
if (shutdown_flag)
{
Firebird::status_exception::raise(isc_shutwarn,
isc_arg_end);
}
#endif /* !SUPERCLIENT && !REQUESTER */
SUBSYSTEM_USAGE_INCR;
ptr = status;
/* copy the file name to a temp buffer, since some of the following
utilities can modify it */
Firebird::PathName temp_filename(file_name,
file_length ? file_length : strlen(file_name));
temp_filename.rtrim();
file_length = temp_filename.length();
Firebird::PathName expanded_filename;
if (!ISC_check_if_remote(temp_filename, true))
{
Firebird::PathName database_filename;
if (ResolveDatabaseAlias(temp_filename, database_filename))
{
ISC_expand_filename(database_filename, false);
expanded_filename = database_filename;
}
else if (set_path(temp_filename, expanded_filename))
{
temp_filename = expanded_filename;
file_length = temp_filename.length();
}
else
{
expanded_filename = temp_filename;
ISC_expand_filename(expanded_filename, true);
}
}
else
{
expanded_filename = temp_filename;
ISC_expand_filename(expanded_filename, true);
}
Firebird::ClumpletWriter newDpb(Firebird::ClumpletReader::Tagged, MAX_DPB_SIZE,
reinterpret_cast<const UCHAR*>(dpb), dpb_length, isc_dpb_version1);
setLogin(newDpb);
for (n = 0; n < SUBSYSTEMS; n++)
{
if (why_enabled && !(why_enabled & (1 << n)))
{
continue;
}
if (!CALL(PROC_ATTACH_DATABASE, n) (ptr, temp_filename.length(),
temp_filename.c_str(),
&handle, newDpb.getBufferLength(),
reinterpret_cast<const char*>(newDpb.getBuffer()),
expanded_filename.c_str()))
{
database = new Attachment(handle, public_handle, n);
database->db_path = expanded_filename;
status[0] = isc_arg_gds;
status[1] = 0;
/* Check to make sure that status[2] is not a warning. If it is, then
* the rest of the vector should be untouched. If it is not, then make
* this element isc_arg_end
*
* This cleanup is done to remove any erroneous errors from trying multiple
* protocols for a database attach
*/
if (status[2] != isc_arg_warning) {
status[2] = isc_arg_end;
}
return status[1];
}
if (ptr[1] != isc_unavailable)
{
ptr = temp;
}
}
}
catch(const Firebird::Exception& e)
{
if (handle)
{
CALL(PROC_DETACH, n) (temp, handle);
}
if (database)
{
delete database;
}
e.stuff_exception(status);
}
SUBSYSTEM_USAGE_DECR;
return status[1];
}
ISC_STATUS API_ROUTINE GDS_BLOB_INFO(ISC_STATUS* user_status,
FB_API_HANDLE* blob_handle,
SSHORT item_length,
const SCHAR* items,
SSHORT buffer_length,
SCHAR* buffer)
{
/**************************************
*
* g d s _ $ b l o b _ i n f o
*
**************************************
*
* Functional description
* Provide information on blob object.
*
**************************************/
YEntry status(user_status);
try
{
Blob* blob = translate<Blob>(blob_handle);
status.setPrimaryHandle(blob);
CALL(PROC_BLOB_INFO, blob->implementation) (status, &blob->handle,
item_length, items,
buffer_length, buffer);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_CANCEL_BLOB(ISC_STATUS * user_status,
FB_API_HANDLE * blob_handle)
{
/**************************************
*
* g d s _ $ c a n c e l _ b l o b
*
**************************************
*
* Functional description
* Abort a partially completed blob.
*
**************************************/
if (!*blob_handle)
{
if (user_status)
{
user_status[0] = isc_arg_gds;
user_status[1] = 0;
user_status[2] = isc_arg_end;
}
return FB_SUCCESS;
}
YEntry status(user_status);
try
{
Blob* blob = translate<Blob>(blob_handle);
status.setPrimaryHandle(blob);
if (! CALL(PROC_CANCEL_BLOB, blob->implementation) (status, &blob->handle))
{
status.setPrimaryHandle(0);
delete blob;
*blob_handle = 0;
}
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_CANCEL_EVENTS(ISC_STATUS * user_status,
FB_API_HANDLE * handle,
SLONG * id)
{
/**************************************
*
* g d s _ $ c a n c e l _ e v e n t s
*
**************************************
*
* Functional description
* Try to cancel an event.
*
**************************************/
YEntry status(user_status);
try
{
Attachment* database = translate<Attachment>(handle);
status.setPrimaryHandle(database);
CALL(PROC_CANCEL_EVENTS, database->implementation) (status,
&database->handle,
id);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
#ifdef CANCEL_OPERATION
ISC_STATUS API_ROUTINE GDS_CANCEL_OPERATION(ISC_STATUS * user_status,
FB_API_HANDLE * handle,
USHORT option)
{
/**************************************
*
* g d s _ $ c a n c e l _ o p e r a t i o n
*
**************************************
*
* Functional description
* Try to cancel an operation.
*
**************************************/
YEntry status(user_status);
try
{
Attachment* database = translate<Attachment>(handle);
status.setPrimaryHandle(database);
CALL(PROC_CANCEL_OPERATION, database->implementation) (status,
&database->handle,
option);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
#endif
ISC_STATUS API_ROUTINE GDS_CLOSE_BLOB(ISC_STATUS * user_status,
FB_API_HANDLE * blob_handle)
{
/**************************************
*
* g d s _ $ c l o s e _ b l o b
*
**************************************
*
* Functional description
* Abort a partially completed blob.
*
**************************************/
YEntry status(user_status);
try
{
Blob* blob = translate<Blob>(blob_handle);
status.setPrimaryHandle(blob);
CALL(PROC_CLOSE_BLOB, blob->implementation) (status, &blob->handle);
status.setPrimaryHandle(0);
delete blob;
*blob_handle = 0;
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_COMMIT(ISC_STATUS * user_status,
FB_API_HANDLE * tra_handle)
{
/**************************************
*
* g d s _ $ c o m m i t
*
**************************************
*
* Functional description
* Commit a transaction.
*
**************************************/
YEntry status(user_status);
try
{
Transaction* transaction = translate<Transaction>(tra_handle);
Transaction* sub;
status.setPrimaryHandle(transaction);
if (transaction->implementation != SUBSYSTEMS) {
// Handle single transaction case
if (CALL(PROC_COMMIT, transaction->implementation) (status,
&transaction->
handle))
{
return status[1];
}
}
else {
// Handle two phase commit. Start by putting everybody into
// limbo. If anybody fails, punt
if (!(transaction->flags & HANDLE_TRANSACTION_limbo))
{
if (prepare(status, transaction))
{
return status[1];
}
}
// Everybody is in limbo, now commit everybody.
// In theory, this can't fail
for (sub = transaction->next; sub; sub = sub->next)
{
if (CALL(PROC_COMMIT, sub->implementation) (status, &sub->handle))
{
return status[1];
}
}
}
status.setPrimaryHandle(0);
while (sub = transaction) {
transaction = sub->next;
delete sub;
}
*tra_handle = 0;
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_COMMIT_RETAINING(ISC_STATUS * user_status,
FB_API_HANDLE * tra_handle)
{
/**************************************
*
* g d s _ $ c o m m i t _ r e t a i n i n g
*
**************************************
*
* Functional description
* Do a commit retaining.
*
* N.B., the transaction cleanup handlers are NOT
* called here since, conceptually, the transaction
* is still running.
*
**************************************/
YEntry status(user_status);
try
{
Transaction* transaction = translate<Transaction>(tra_handle);
status.setPrimaryHandle(transaction);
for (Transaction* sub = transaction; sub; sub = sub->next)
{
if (sub->implementation != SUBSYSTEMS &&
CALL(PROC_COMMIT_RETAINING, sub->implementation) (status,
&sub->handle))
{
return status[1];
}
}
transaction->flags |= HANDLE_TRANSACTION_limbo;
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_COMPILE(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* req_handle,
USHORT blr_length,
const SCHAR* blr)
{
/**************************************
*
* g d s _ $ c o m p i l e
*
**************************************
*
* Functional description
*
**************************************/
YEntry status(user_status);
Attachment* dbb = 0;
StReq* rq = 0;
try
{
dbb = translate<Attachment>(db_handle);
status.setPrimaryHandle(dbb);
nullCheck(req_handle, isc_bad_req_handle);
if (CALL(PROC_COMPILE, dbb->implementation) (status, &dbb->handle,
&rq, blr_length,
blr))
{
return status[1];
}
new Request(rq, req_handle, dbb);
}
catch (const Firebird::Exception& e)
{
*req_handle = 0;
if (dbb && rq)
{
CALL(PROC_RELEASE_REQUEST, dbb->implementation) (status, rq);
}
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_COMPILE2(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* req_handle,
USHORT blr_length,
const SCHAR* blr)
{
/**************************************
*
* g d s _ $ c o m p i l e 2
*
**************************************
*
* Functional description
*
**************************************/
Status status(user_status);
try
{
if (GDS_COMPILE(status, db_handle, req_handle, blr_length, blr))
{
return status[1];
}
Request *request = translate<Request>(req_handle);
request->user_handle = req_handle;
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_CREATE_BLOB(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
FB_API_HANDLE* blob_handle,
SLONG* blob_id)
{
/**************************************
*
* g d s _ $ c r e a t e _ b l o b
*
**************************************
*
* Functional description
* Open an existing blob.
*
**************************************/
return open_blob(user_status, db_handle, tra_handle, blob_handle, blob_id,
0, 0, PROC_CREATE_BLOB, PROC_CREATE_BLOB2);
}
ISC_STATUS API_ROUTINE GDS_CREATE_BLOB2(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
FB_API_HANDLE* blob_handle,
SLONG* blob_id,
SSHORT bpb_length,
const UCHAR* bpb)
{
/**************************************
*
* g d s _ $ c r e a t e _ b l o b 2
*
**************************************
*
* Functional description
* Open an existing blob.
*
**************************************/
return open_blob(user_status, db_handle, tra_handle, blob_handle, blob_id,
bpb_length, bpb, PROC_CREATE_BLOB,
PROC_CREATE_BLOB2);
}
ISC_STATUS API_ROUTINE GDS_CREATE_DATABASE(ISC_STATUS* user_status,
USHORT file_length,
const TEXT* file_name,
FB_API_HANDLE* public_handle,
SSHORT dpb_length,
const UCHAR* dpb,
USHORT db_type)
{
/**************************************
*
* g d s _ $ c r e a t e _ d a t a b a s e
*
**************************************
*
* Functional description
* Create a nice, squeaky clean database, uncorrupted by user data.
*
**************************************/
ISC_STATUS *ptr;
ISC_STATUS_ARRAY temp;
StAtt* handle = 0;
Attachment* database = 0;
USHORT n;
YEntry status(user_status);
try
{
nullCheck(public_handle, isc_bad_db_handle);
if (!file_name)
{
Firebird::status_exception::raise(isc_bad_db_format,
isc_arg_string,
"",
isc_arg_end);
}
if (dpb_length > 0 && !dpb)
{
Firebird::status_exception::raise(isc_bad_dpb_form,
isc_arg_end);
}
#if !defined (SUPERCLIENT) && !defined (REQUESTER)
if (shutdown_flag)
{
Firebird::status_exception::raise(isc_shutwarn,
isc_arg_end);
}
#endif /* !SUPERCLIENT && !REQUESTER */
SUBSYSTEM_USAGE_INCR;
ptr = status;
/* copy the file name to a temp buffer, since some of the following
utilities can modify it */
Firebird::PathName temp_filename(file_name,
file_length ? file_length : strlen(file_name));
temp_filename.rtrim();
file_length = temp_filename.length();
Firebird::PathName expanded_filename;
if (!ISC_check_if_remote(temp_filename, true))
{
Firebird::PathName database_filename;
if (ResolveDatabaseAlias(temp_filename, database_filename))
{
ISC_expand_filename(database_filename, false);
expanded_filename = database_filename;
}
else if (set_path(temp_filename, expanded_filename))
{
temp_filename = expanded_filename;
}
else
{
expanded_filename = temp_filename;
ISC_expand_filename(expanded_filename, true);
}
}
else
{
expanded_filename = temp_filename;
ISC_expand_filename(expanded_filename, true);
}
Firebird::ClumpletWriter newDpb(Firebird::ClumpletReader::Tagged, MAX_DPB_SIZE,
reinterpret_cast<const UCHAR*>(dpb), dpb_length, isc_dpb_version1);
setLogin(newDpb);
for (n = 0; n < SUBSYSTEMS; n++)
{
if (why_enabled && !(why_enabled & (1 << n)))
{
continue;
}
if (!CALL(PROC_CREATE_DATABASE, n) (ptr, temp_filename.length(),
temp_filename.c_str(),
&handle, newDpb.getBufferLength(),
reinterpret_cast<const char*>(newDpb.getBuffer()),
0, expanded_filename.c_str()))
{
#ifdef WIN_NT
/* Now we can expand, the file exists. */
expanded_filename = temp_filename;
ISC_expand_filename (expanded_filename, true);
#endif
database = new Attachment(handle, public_handle, n);
#ifdef WIN_NT
database->db_path = expanded_filename;
#else
database->db_path = temp_filename;
#endif
status[0] = isc_arg_gds;
status[1] = 0;
if (status[2] != isc_arg_warning)
status[2] = isc_arg_end;
return status[1];
}
if (ptr[1] != isc_unavailable)
ptr = temp;
}
}
catch(const Firebird::Exception& e)
{
e.stuff_exception(status);
if (handle)
{
CALL(PROC_DROP_DATABASE, n) (temp, handle);
}
if (database)
{
delete database;
}
}
SUBSYSTEM_USAGE_DECR;
return status[1];
}
ISC_STATUS API_ROUTINE isc_database_cleanup(ISC_STATUS * user_status,
FB_API_HANDLE * handle,
AttachmentCleanupRoutine * routine,
void* arg)
{
/**************************************
*
* g d s _ $ d a t a b a s e _ c l e a n u p
*
**************************************
*
* Functional description
* Register a database specific cleanup handler.
*
**************************************/
YEntry status(user_status);
try
{
Attachment* database = translate<Attachment>(handle);
status.setPrimaryHandle(database);
database->cleanup.add(routine, arg);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_DATABASE_INFO(ISC_STATUS* user_status,
FB_API_HANDLE* handle,
SSHORT item_length,
const SCHAR* items,
SSHORT buffer_length,
SCHAR* buffer)
{
/**************************************
*
* g d s _ $ d a t a b a s e _ i n f o
*
**************************************
*
* Functional description
* Provide information on database object.
*
**************************************/
YEntry status(user_status);
try
{
Attachment* database = translate<Attachment>(handle);
status.setPrimaryHandle(database);
CALL(PROC_DATABASE_INFO, database->implementation) (status,
&database->handle,
item_length,
items,
buffer_length,
buffer);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_DDL(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
SSHORT length,
const UCHAR* ddl)
{
/**************************************
*
* g d s _ $ d d l
*
**************************************
*
* Functional description
* Do meta-data update.
*
**************************************/
YEntry status(user_status);
try
{
Attachment* database = translate<Attachment>(db_handle);
status.setPrimaryHandle(database);
Transaction* transaction = findTransaction(tra_handle, database);
CALL(PROC_DDL, database->implementation) (status,
&database->handle,
&transaction->handle,
length, ddl);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_DETACH(ISC_STATUS * user_status,
FB_API_HANDLE * handle)
{
/**************************************
*
* g d s _ $ d e t a c h
*
**************************************
*
* Functional description
* Close down a database.
*
**************************************/
YEntry status(user_status);
try
{
Attachment* dbb = translate<Attachment>(handle);
size_t i;
#if defined(SUPERSERVER) && !defined(EMBEDDED)
// Drop all DSQL statements to reclaim DSQL memory pools.
while ((i = dbb->statements.getCount()))
{
FB_API_HANDLE temp_handle;
Statement* statement = dbb->statements[i - 1];
if (!statement->user_handle) {
temp_handle = statement->public_handle;
statement->user_handle = &temp_handle;
}
subsystem_exit();
ISC_STATUS rc = GDS_DSQL_FREE(status, statement->user_handle, DSQL_drop);
subsystem_enter();
if (rc)
{
return status[1];
}
}
#endif
if (CALL(PROC_DETACH, dbb->implementation) (status, &dbb->handle))
return status[1];
// Release associated request handles
while ((i = dbb->requests.getCount()))
{
delete dbb->requests[i - 1];
}
#ifndef SUPERSERVER
while ((i = dbb->statements.getCount()))
{
release_dsql_support(dbb->statements[i - 1]->das);
delete dbb->statements[i - 1];
}
#endif
while ((i = dbb->blobs.getCount()))
{
delete dbb->blobs[i - 1];
}
SUBSYSTEM_USAGE_DECR;
delete dbb;
*handle = 0;
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
int API_ROUTINE gds__disable_subsystem(TEXT * subsystem)
{
/**************************************
*
* g d s _ $ d i s a b l e _ s u b s y s t e m
*
**************************************
*
* Functional description
* Disable access to a specific subsystem. If no subsystem
* has been explicitly disabled, all are available.
*
**************************************/
const IMAGE* sys = images;
for (const IMAGE* const end = sys + SUBSYSTEMS; sys < end; sys++)
{
if (!strcmp(sys->name, subsystem)) {
if (!why_enabled)
why_enabled = ~why_enabled;
why_enabled &= ~(1 << (sys - images));
return TRUE;
}
}
return FALSE;
}
ISC_STATUS API_ROUTINE GDS_DROP_DATABASE(ISC_STATUS * user_status,
FB_API_HANDLE * handle)
{
/**************************************
*
* i s c _ d r o p _ d a t a b a s e
*
**************************************
*
* Functional description
* Close down a database and then purge it.
*
**************************************/
YEntry status(user_status);
try
{
Attachment* dbb = translate<Attachment>(handle);
size_t i;
#if defined(SUPERSERVER) && !defined(EMBEDDED)
// Drop all DSQL statements to reclaim DSQL memory pools.
while ((i = dbb->statements.getCount()))
{
FB_API_HANDLE temp_handle;
Statement* statement = dbb->statements[i - 1];
if (!statement->user_handle) {
temp_handle = statement->public_handle;
statement->user_handle = &temp_handle;
}
subsystem_exit();
ISC_STATUS rc = GDS_DSQL_FREE(status, statement->user_handle, DSQL_drop);
subsystem_enter();
if (rc)
{
return status[1];
}
}
#endif
CALL(PROC_DROP_DATABASE, dbb->implementation) (status, &dbb->handle);
if (status[1] && (status[1] != isc_drdb_completed_with_errs))
return status[1];
// Release associated request handles
while ((i = dbb->requests.getCount()))
{
delete dbb->requests[i - 1];
}
#ifndef SUPERSERVER
while ((i = dbb->statements.getCount()))
{
release_dsql_support(dbb->statements[i - 1]->das);
delete dbb->statements[i - 1];
}
#endif
while ((i = dbb->blobs.getCount()))
{
delete dbb->blobs[i - 1];
}
SUBSYSTEM_USAGE_DECR;
delete dbb;
*handle = 0;
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_DSQL_ALLOC(ISC_STATUS * user_status,
FB_API_HANDLE * db_handle,
FB_API_HANDLE * stmt_handle)
{
/**************************************
*
* i s c _ d s q l _ a l l o c _ s t a t e m e n t
*
**************************************/
return GDS_DSQL_ALLOCATE(user_status, db_handle, stmt_handle);
}
ISC_STATUS API_ROUTINE GDS_DSQL_ALLOC2(ISC_STATUS * user_status,
FB_API_HANDLE * db_handle,
FB_API_HANDLE * stmt_handle)
{
/**************************************
*
* i s c _ d s q l _ a l l o c _ s t a t e m e n t 2
*
**************************************
*
* Functional description
* Allocate a statement handle.
*
**************************************/
Status status(user_status);
try
{
if (GDS_DSQL_ALLOCATE(user_status, db_handle, stmt_handle))
{
return status[1];
}
Statement *statement = translate<Statement>(stmt_handle);
statement->user_handle = stmt_handle;
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_DSQL_ALLOCATE(ISC_STATUS * user_status,
FB_API_HANDLE * db_handle,
FB_API_HANDLE * public_stmt_handle)
{
/**************************************
*
* i s c _ d s q l _ a l l o c a t e _ s t a t e m e n t
*
**************************************
*
* Functional description
* Allocate a statement handle.
*
**************************************/
YEntry status(user_status);
Attachment* dbb = 0;
StStm* stmt_handle = 0;
UCHAR flag = 0;
try
{
dbb = translate<Attachment>(db_handle);
status.setPrimaryHandle(dbb);
// check the statement handle to make sure it's NULL and then initialize it.
nullCheck(public_stmt_handle, isc_bad_stmt_handle);
/* Attempt to have the implementation which processed the database attach
process the allocate statement. This may not be feasible (e.g., the
server doesn't support remote DSQL because it's the wrong version or
something) in which case, execute the functionality locally (and hence
remotely through the original Y-valve). */
ISC_STATUS s = isc_unavailable;
PTR entry = get_entrypoint(PROC_DSQL_ALLOCATE, dbb->implementation);
if (entry != no_entrypoint)
{
s = (*entry) (status, &dbb->handle, &stmt_handle);
}
#ifndef NO_LOCAL_DSQL
if (s == isc_unavailable) {
// if the entry point didn't exist or if the routine said the server
// didn't support the protocol... do it locally
flag = HANDLE_STATEMENT_local;
s = dsql8_allocate_statement(status, db_handle, &stmt_handle);
}
#endif
if (status[1])
{
return status[1];
}
Statement* statement = new Statement(stmt_handle, public_stmt_handle, dbb);
statement->flags = flag;
}
catch (const Firebird::Exception& e)
{
*public_stmt_handle = 0;
if (dbb && stmt_handle)
{
#ifndef NO_LOCAL_DSQL
if (flag & HANDLE_STATEMENT_local)
dsql8_free_statement(status, &stmt_handle, DSQL_drop);
else
#endif
CALL(PROC_DSQL_FREE, dbb->implementation) (status, stmt_handle,
DSQL_drop);
}
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE isc_dsql_describe(ISC_STATUS * user_status,
FB_API_HANDLE * stmt_handle,
USHORT dialect,
XSQLDA * sqlda)
{
/**************************************
*
* i s c _ d s q l _ d e s c r i b e
*
**************************************
*
* Functional description
* Describe output parameters for a prepared statement.
*
**************************************/
Status status(user_status);
try
{
Statement* statement = translate<Statement>(stmt_handle);
statement->checkPrepared();
sqlda_sup::dasup_clause& clause =
statement->das.dasup_clauses[DASUP_CLAUSE_select];
if (clause.dasup_info_len && clause.dasup_info_buf)
{
iterative_sql_info( status,
stmt_handle,
sizeof(describe_select_info),
describe_select_info,
clause.dasup_info_len,
clause.dasup_info_buf,
dialect,
sqlda);
}
else
{
Firebird::HalfStaticArray<SCHAR, DESCRIBE_BUFFER_SIZE> local_buffer;
USHORT buffer_len = sqlda_buffer_size(DESCRIBE_BUFFER_SIZE, sqlda, dialect);
SCHAR *buffer = local_buffer.getBuffer(buffer_len);
if (!GDS_DSQL_SQL_INFO( status,
stmt_handle,
sizeof(describe_select_info),
describe_select_info,
buffer_len,
buffer))
{
iterative_sql_info( status,
stmt_handle,
sizeof(describe_select_info),
describe_select_info,
buffer_len,
buffer,
dialect,
sqlda);
}
}
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE isc_dsql_describe_bind(ISC_STATUS * user_status,
FB_API_HANDLE * stmt_handle,
USHORT dialect,
XSQLDA * sqlda)
{
/**************************************
*
* i s c _ d s q l _ d e s c r i b e _ b i n d
*
**************************************
*
* Functional description
* Describe input parameters for a prepared statement.
*
**************************************/
Status status(user_status);
try
{
Statement* statement = translate<Statement>(stmt_handle);
sqlda_sup::dasup_clause& clause =
statement->das.dasup_clauses[DASUP_CLAUSE_bind];
if (clause.dasup_info_len && clause.dasup_info_buf)
{
iterative_sql_info( status,
stmt_handle,
sizeof(describe_bind_info),
describe_bind_info,
clause.dasup_info_len,
clause.dasup_info_buf,
dialect,
sqlda);
}
else
{
Firebird::HalfStaticArray<SCHAR, DESCRIBE_BUFFER_SIZE> local_buffer;
USHORT buffer_len = sqlda_buffer_size(DESCRIBE_BUFFER_SIZE, sqlda, dialect);
SCHAR *buffer = local_buffer.getBuffer(buffer_len);
if (!GDS_DSQL_SQL_INFO( status,
stmt_handle,
sizeof(describe_bind_info),
describe_bind_info,
buffer_len,
buffer))
{
iterative_sql_info( status,
stmt_handle,
sizeof(describe_bind_info),
describe_bind_info,
buffer_len,
buffer,
dialect,
sqlda);
}
}
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_DSQL_EXECUTE(ISC_STATUS* user_status,
FB_API_HANDLE* tra_handle,
FB_API_HANDLE* stmt_handle,
USHORT dialect,
XSQLDA* sqlda)
{
/**************************************
*
* i s c _ d s q l _ e x e c u t e
*
**************************************
*
* Functional description
* Execute a non-SELECT dynamic SQL statement.
*
**************************************/
return GDS_DSQL_EXECUTE2(user_status, tra_handle, stmt_handle, dialect,
sqlda, NULL);
}
ISC_STATUS API_ROUTINE GDS_DSQL_EXECUTE2(ISC_STATUS* user_status,
FB_API_HANDLE* tra_handle,
FB_API_HANDLE* stmt_handle,
USHORT dialect,
XSQLDA* in_sqlda,
XSQLDA* out_sqlda)
{
/**************************************
*
* i s c _ d s q l _ e x e c u t e 2
*
**************************************
*
* Functional description
* Execute a non-SELECT dynamic SQL statement.
*
**************************************/
Status status(user_status);
try
{
USHORT in_blr_length, in_msg_type, in_msg_length,
out_blr_length, out_msg_type, out_msg_length;
Statement* statement = translate<Statement>(stmt_handle);
sqlda_sup* dasup = &(statement->das);
statement->checkPrepared();
if (UTLD_parse_sqlda(status, dasup, &in_blr_length, &in_msg_type,
&in_msg_length, dialect, in_sqlda,
DASUP_CLAUSE_bind))
{
return status[1];
}
if (UTLD_parse_sqlda
(status, dasup, &out_blr_length, &out_msg_type, &out_msg_length,
dialect, out_sqlda, DASUP_CLAUSE_select))
{
return status[1];
}
if (GDS_DSQL_EXECUTE2_M(status, tra_handle, stmt_handle,
in_blr_length,
dasup->dasup_clauses[DASUP_CLAUSE_bind].dasup_blr,
in_msg_type, in_msg_length,
dasup->dasup_clauses[DASUP_CLAUSE_bind].dasup_msg,
out_blr_length,
dasup->dasup_clauses[DASUP_CLAUSE_select].
dasup_blr, out_msg_type, out_msg_length,
dasup->dasup_clauses[DASUP_CLAUSE_select].
dasup_msg))
{
return status[1];
}
if (UTLD_parse_sqlda(status, dasup, NULL, NULL, NULL,
dialect, out_sqlda, DASUP_CLAUSE_select))
{
return status[1];
}
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_DSQL_EXECUTE_M(ISC_STATUS* user_status,
FB_API_HANDLE* tra_handle,
FB_API_HANDLE* stmt_handle,
USHORT blr_length,
const SCHAR* blr,
USHORT msg_type,
USHORT msg_length,
SCHAR* msg)
{
/**************************************
*
* i s c _ d s q l _ e x e c u t e _ m
*
**************************************
*
* Functional description
* Execute a non-SELECT dynamic SQL statement.
*
**************************************/
return GDS_DSQL_EXECUTE2_M(user_status, tra_handle, stmt_handle,
blr_length, blr, msg_type, msg_length, msg,
0, NULL, 0, 0, NULL);
}
ISC_STATUS API_ROUTINE GDS_DSQL_EXECUTE2_M(ISC_STATUS* user_status,
FB_API_HANDLE* tra_handle,
FB_API_HANDLE* stmt_handle,
USHORT in_blr_length,
const SCHAR* in_blr,
USHORT in_msg_type,
USHORT in_msg_length,
SCHAR* in_msg,
USHORT out_blr_length,
SCHAR* out_blr,
USHORT out_msg_type,
USHORT out_msg_length,
SCHAR* out_msg)
{
/**************************************
*
* i s c _ d s q l _ e x e c u t e 2 _ m
*
**************************************
*
* Functional description
* Execute a non-SELECT dynamic SQL statement.
*
**************************************/
YEntry status(user_status);
try
{
Statement* statement = translate<Statement>(stmt_handle);
status.setPrimaryHandle(statement);
Transaction* transaction = 0;
if (*tra_handle)
{
transaction = translate<Transaction>(tra_handle);
}
StTra *handle = 0;
PTR entry;
#ifndef NO_LOCAL_DSQL
if (statement->flags & HANDLE_STATEMENT_local) {
dsql8_execute(status, tra_handle, &statement->handle,
in_blr_length, in_blr, in_msg_type, in_msg_length,
in_msg, out_blr_length, out_blr, out_msg_type,
out_msg_length, out_msg);
}
else
#endif
{
if (transaction) {
Transaction *t = find_transaction(statement->parent, transaction);
if (!t)
{
bad_handle(isc_bad_trans_handle);
}
handle = t->handle;
}
entry = get_entrypoint(PROC_DSQL_EXECUTE2, statement->implementation);
if (entry != no_entrypoint &&
(*entry) (status,
&handle,
&statement->handle,
in_blr_length,
in_blr,
in_msg_type,
in_msg_length,
in_msg,
out_blr_length,
out_blr,
out_msg_type,
out_msg_length, out_msg) != isc_unavailable);
else if (!out_blr_length && !out_msg_type && !out_msg_length)
CALL(PROC_DSQL_EXECUTE, statement->implementation) (status,
&handle,
&statement->handle,
in_blr_length,
in_blr,
in_msg_type,
in_msg_length,
in_msg);
else
no_entrypoint(status);
if (!status[1])
{
if (transaction && !handle) {
delete transaction;
*tra_handle = 0;
}
else if (!transaction && handle)
{
transaction = new Transaction(handle, tra_handle, statement->parent);
}
}
}
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_DSQL_EXEC_IMMED(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
USHORT length,
const SCHAR* string,
USHORT dialect,
XSQLDA* sqlda)
{
/**************************************
*
* i s c _ d s q l _ e x e c _ i m m e d
*
**************************************
*
* Functional description
*
**************************************/
return GDS_DSQL_EXECUTE_IMMED(user_status,
db_handle, tra_handle, length, string,
dialect, sqlda);
}
ISC_STATUS API_ROUTINE GDS_DSQL_EXECUTE_IMMED(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
USHORT length,
const SCHAR* string,
USHORT dialect, XSQLDA* sqlda)
{
/**************************************
*
* i s c _ d s q l _ e x e c u t e _ i m m e d i a t e
*
**************************************
*
* Functional description
* Prepare a statement for execution.
*
**************************************/
return GDS_DSQL_EXEC_IMMED2(user_status,
db_handle, tra_handle, length, string,
dialect, sqlda, NULL);
}
ISC_STATUS API_ROUTINE GDS_DSQL_EXEC_IMMED2(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
USHORT length,
const SCHAR* string,
USHORT dialect,
XSQLDA* in_sqlda,
XSQLDA* out_sqlda)
{
/**************************************
*
* i s c _ d s q l _ e x e c _ i m m e d 2
*
**************************************
*
* Functional description
* Prepare a statement for execution.
*
**************************************/
Status status(user_status);
ISC_STATUS s = 0;
sqlda_sup dasup;
memset(&dasup, 0, sizeof(sqlda_sup));
try
{
USHORT in_blr_length, in_msg_type, in_msg_length,
out_blr_length, out_msg_type, out_msg_length;
if (UTLD_parse_sqlda(status, &dasup, &in_blr_length, &in_msg_type,
&in_msg_length, dialect, in_sqlda,
DASUP_CLAUSE_bind))
{
return status[1];
}
if (UTLD_parse_sqlda
(status, &dasup, &out_blr_length, &out_msg_type, &out_msg_length,
dialect, out_sqlda, DASUP_CLAUSE_select))
{
return status[1];
}
s = GDS_DSQL_EXEC_IMM2_M(status, db_handle, tra_handle,
length, string, dialect,
in_blr_length,
dasup.dasup_clauses[DASUP_CLAUSE_bind].dasup_blr,
in_msg_type, in_msg_length,
dasup.dasup_clauses[DASUP_CLAUSE_bind].dasup_msg,
out_blr_length,
dasup.dasup_clauses[DASUP_CLAUSE_select].dasup_blr,
out_msg_type, out_msg_length,
dasup.dasup_clauses[DASUP_CLAUSE_select].dasup_msg);
status.ok();
if (!s)
{
s = UTLD_parse_sqlda(status, &dasup, NULL, NULL, NULL, dialect,
out_sqlda, DASUP_CLAUSE_select);
}
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
s = status[1];
}
release_dsql_support(dasup);
return s;
}
ISC_STATUS API_ROUTINE GDS_DSQL_EXEC_IMM_M(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
USHORT length,
const SCHAR* string,
USHORT dialect,
USHORT blr_length,
USHORT msg_type,
USHORT msg_length,
const SCHAR* blr,
SCHAR* msg)
{
/**************************************
*
* i s c _ d s q l _ e x e c _ i m m e d _ m
*
**************************************
*
* Functional description
*
**************************************/
return GDS_DSQL_EXECUTE_IMM_M(user_status, db_handle, tra_handle,
length, string, dialect, blr_length, blr,
msg_type, msg_length, msg);
}
ISC_STATUS API_ROUTINE GDS_DSQL_EXECUTE_IMM_M(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
USHORT length,
const SCHAR* string,
USHORT dialect,
USHORT blr_length,
const SCHAR* blr,
USHORT msg_type,
USHORT msg_length,
SCHAR* msg)
{
/**************************************
*
* i s c _ d s q l _ e x e c u t e _ i m m e d i a t e _ m
*
**************************************
*
* Functional description
* Prepare a statement for execution.
*
**************************************/
return GDS_DSQL_EXEC_IMM2_M(user_status, db_handle, tra_handle,
length, string, dialect, blr_length, blr,
msg_type, msg_length, msg, 0, NULL, 0, 0,
NULL);
}
ISC_STATUS API_ROUTINE GDS_DSQL_EXEC_IMM2_M(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
USHORT length,
const SCHAR* string,
USHORT dialect,
USHORT in_blr_length,
const SCHAR* in_blr,
USHORT in_msg_type,
USHORT in_msg_length,
SCHAR* in_msg,
USHORT out_blr_length,
SCHAR* out_blr,
USHORT out_msg_type,
USHORT out_msg_length,
SCHAR* out_msg)
{
/**************************************
*
* i s c _ d s q l _ e x e c _ i m m 2 _ m
*
**************************************
*
* Functional description
* Prepare a statement for execution.
*
**************************************/
Status status(user_status);
bool stmt_eaten;
if (PREPARSE_execute( status,
db_handle,
tra_handle,
length,
string,
&stmt_eaten,
dialect))
{
if (status[1])
{
return status[1];
}
ISC_STATUS_ARRAY temp_status;
FB_API_HANDLE crdb_trans_handle = 0;
if (GDS_START_TRANSACTION(status, &crdb_trans_handle, 1,
db_handle, 0, 0))
{
save_error_string(status);
GDS_DROP_DATABASE(temp_status, db_handle);
*db_handle = 0;
return status[1];
}
BOOLEAN ret_v3_error = FALSE;
if (!stmt_eaten) {
/* Check if against < 4.0 database */
const SCHAR ch = isc_info_base_level;
SCHAR buffer[16];
if (!GDS_DATABASE_INFO(status, db_handle, 1, &ch, sizeof(buffer),
buffer))
{
if ((buffer[0] != isc_info_base_level) || (buffer[4] > 3))
GDS_DSQL_EXEC_IMM3_M(status, db_handle,
&crdb_trans_handle, length, string,
dialect, in_blr_length, in_blr,
in_msg_type, in_msg_length, in_msg,
out_blr_length, out_blr,
out_msg_type, out_msg_length,
out_msg);
else
ret_v3_error = TRUE;
}
}
if (status[1]) {
GDS_ROLLBACK(temp_status, &crdb_trans_handle);
save_error_string(status);
GDS_DROP_DATABASE(temp_status, db_handle);
*db_handle = 0;
return status[1];
}
if (GDS_COMMIT(status, &crdb_trans_handle)) {
GDS_ROLLBACK(temp_status, &crdb_trans_handle);
save_error_string(status);
GDS_DROP_DATABASE(temp_status, db_handle);
*db_handle = 0;
return status[1];
}
if (ret_v3_error) {
status[0] = isc_arg_gds;
status[1] = isc_srvr_version_too_old;
status[2] = isc_arg_end;
return status[1];
}
return status[1];
}
return GDS_DSQL_EXEC_IMM3_M(user_status, db_handle, tra_handle,
length, string, dialect,
in_blr_length, in_blr, in_msg_type,
in_msg_length, in_msg, out_blr_length,
out_blr, out_msg_type, out_msg_length,
out_msg);
}
ISC_STATUS API_ROUTINE GDS_DSQL_EXEC_IMM3_M(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
USHORT length,
const SCHAR* string,
USHORT dialect,
USHORT in_blr_length,
const SCHAR* in_blr,
USHORT in_msg_type,
USHORT in_msg_length,
SCHAR* in_msg,
USHORT out_blr_length,
SCHAR* out_blr,
USHORT out_msg_type,
USHORT out_msg_length,
SCHAR* out_msg)
{
/**************************************
*
* i s c _ d s q l _ e x e c _ i m m 3 _ m
*
**************************************
*
* Functional description
* Prepare a statement for execution.
*
**************************************/
YEntry status(user_status);
try
{
Attachment* dbb = translate<Attachment>(db_handle);
status.setPrimaryHandle(dbb);
Transaction* transaction = 0;
StTra* handle = 0;
if (*tra_handle)
{
transaction = find_transaction(dbb, translate<Transaction>(tra_handle));
Transaction* t = find_transaction(dbb, transaction);
if (!t)
{
bad_handle(isc_bad_trans_handle);
}
handle = t->handle;
}
/* Attempt to have the implementation which processed the database attach
process the prepare statement. This may not be feasible (e.g., the
server doesn't support remote DSQL because it's the wrong version or
something) in which case, execute the functionality locally (and hence
remotely through the original Y-valve). */
ISC_STATUS s = isc_unavailable;
PTR entry = get_entrypoint(PROC_DSQL_EXEC_IMMED2, dbb->implementation);
if (entry != no_entrypoint) {
s = (*entry) (status, &dbb->handle, &handle,
length, string, dialect,
in_blr_length, in_blr,
in_msg_type, in_msg_length, in_msg,
out_blr_length, out_blr,
out_msg_type, out_msg_length, out_msg);
}
if (s == isc_unavailable && !out_msg_length) {
entry = get_entrypoint(PROC_DSQL_EXEC_IMMED, dbb->implementation);
if (entry != no_entrypoint)
{
s = (*entry) (status, &dbb->handle, &handle,
length, string, dialect,
in_blr_length, in_blr,
in_msg_type, in_msg_length, in_msg);
}
}
if (s != isc_unavailable && !status[1])
{
if (transaction && !handle) {
delete transaction;
*tra_handle = 0;
}
else if (!transaction && handle) {
transaction = new Transaction(handle, tra_handle, dbb);
}
}
#ifndef NO_LOCAL_DSQL
if (s == isc_unavailable) {
// if the entry point didn't exist or if the routine said the server
// didn't support the protocol... do it locally
dsql8_execute_immediate(status, db_handle, tra_handle,
length, string, dialect,
in_blr_length, in_blr, in_msg_type,
in_msg_length, in_msg, out_blr_length,
out_blr, out_msg_type, out_msg_length,
out_msg);
}
#endif
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_DSQL_FETCH(ISC_STATUS* user_status,
FB_API_HANDLE* stmt_handle,
USHORT dialect,
XSQLDA* sqlda)
{
/**************************************
*
* i s c _ d s q l _ f e t c h
*
**************************************
*
* Functional description
* Fetch next record from a dynamic SQL cursor
*
**************************************/
Status status(user_status);
try
{
if (!sqlda)
{
Firebird::status_exception::raise(isc_dsql_sqlda_err, isc_arg_end);
}
Statement* statement = translate<Statement>(stmt_handle);
statement->checkPrepared();
sqlda_sup& dasup = statement->das;
USHORT blr_length, msg_type, msg_length;
if (UTLD_parse_sqlda(status, &dasup, &blr_length, &msg_type, &msg_length,
dialect, sqlda, DASUP_CLAUSE_select))
{
return status[1];
}
ISC_STATUS s = GDS_DSQL_FETCH_M(status, stmt_handle, blr_length,
dasup.dasup_clauses[DASUP_CLAUSE_select].dasup_blr,
0, msg_length,
dasup.dasup_clauses[DASUP_CLAUSE_select].dasup_msg);
if (s && s != 101)
{
status.ok();
return s;
}
if (UTLD_parse_sqlda(status, &dasup, NULL, NULL, NULL,
dialect, sqlda, DASUP_CLAUSE_select))
{
return status[1];
}
status.ok();
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
#ifdef SCROLLABLE_CURSORS
ISC_STATUS API_ROUTINE GDS_DSQL_FETCH2(ISC_STATUS* user_status,
FB_API_HANDLE* stmt_handle,
USHORT dialect,
XSQLDA* sqlda,
USHORT direction,
SLONG offset)
{
/**************************************
*
* i s c _ d s q l _ f e t c h 2
*
**************************************
*
* Functional description
* Fetch next record from a dynamic SQL cursor
*
**************************************/
Status status(user_status);
try
{
if (!sqlda)
{
Firebird::status_exception::raise(isc_dsql_sqlda_err, isc_arg_end);
}
Statement* statement = translate<Statement>(stmt_handle);
statement->checkPrepared();
sqlda_sup& dasup = statement->das;
USHORT blr_length, msg_type, msg_length;
if (UTLD_parse_sqlda(status, &dasup, &blr_length, &msg_type, &msg_length,
dialect, sqlda, DASUP_CLAUSE_select))
{
return status[1];
}
ISC_STATUS s = GDS_DSQL_FETCH2_M(status, stmt_handle, blr_length,
dasup.dasup_clauses[DASUP_CLAUSE_select].dasup_blr,
0, msg_length,
dasup.dasup_clauses[DASUP_CLAUSE_select].dasup_msg,
direction, offset);
if (s && s != 101)
{
status.ok();
return s;
}
if (UTLD_parse_sqlda(status, &dasup, NULL, NULL, NULL,
dialect, sqlda, DASUP_CLAUSE_select))
{
return status[1];
}
status.ok();
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
#endif
ISC_STATUS API_ROUTINE GDS_DSQL_FETCH_M(ISC_STATUS* user_status,
FB_API_HANDLE* stmt_handle,
USHORT blr_length,
const SCHAR* blr,
USHORT msg_type,
USHORT msg_length,
SCHAR* msg)
{
/**************************************
*
* i s c _ d s q l _ f e t c h _ m
*
**************************************
*
* Functional description
* Fetch next record from a dynamic SQL cursor
*
**************************************/
YEntry status(user_status);
try
{
Statement* statement = translate<Statement>(stmt_handle);
status.setPrimaryHandle(statement);
ISC_STATUS s =
#ifndef NO_LOCAL_DSQL
(statement->flags & HANDLE_STATEMENT_local) ?
dsql8_fetch(status, &statement->handle, blr_length, blr,
msg_type, msg_length, msg
#ifdef SCROLLABLE_CURSORS
, (USHORT) 0, (ULONG) 1
#endif // SCROLLABLE_CURSORS
) :
#endif // NO_LOCAL_DSQL
CALL(PROC_DSQL_FETCH, statement->implementation) (status,
&statement->handle,
blr_length, blr,
msg_type,
msg_length, msg
#ifdef SCROLLABLE_CURSORS
,
(USHORT) 0,
(ULONG) 1
#endif // SCROLLABLE_CURSORS
);
if (s == 100 || s == 101)
{
status.ok();
return s;
}
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
#ifdef SCROLLABLE_CURSORS
ISC_STATUS API_ROUTINE GDS_DSQL_FETCH2_M(ISC_STATUS* user_status,
FB_API_HANDLE* stmt_handle,
USHORT blr_length,
const SCHAR* blr,
USHORT msg_type,
USHORT msg_length,
SCHAR* msg,
USHORT direction,
SLONG offset)
{
/**************************************
*
* i s c _ d s q l _ f e t c h 2 _ m
*
**************************************
*
* Functional description
* Fetch next record from a dynamic SQL cursor
*
**************************************/
YEntry status(user_status);
try
{
Statement* statement = translate<Statement>(stmt_handle);
status.setPrimaryHandle(statement);
ISC_STATUS s =
#ifndef NO_LOCAL_DSQL
(statement->flags & HANDLE_STATEMENT_local) ?
dsql8_fetch(status,
&statement->handle, blr_length, blr, msg_type,
msg_length, msg, direction, offset) :
#endif
CALL(PROC_DSQL_FETCH, statement->implementation) (status,
&statement->
handle,
blr_length, blr,
msg_type,
msg_length, msg,
direction,
offset);
if (s == 100 || s == 101)
{
status.ok();
return s;
}
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
#endif
ISC_STATUS API_ROUTINE GDS_DSQL_FREE(ISC_STATUS * user_status,
FB_API_HANDLE * stmt_handle,
USHORT option)
{
/*****************************************
*
* i s c _ d s q l _ f r e e _ s t a t e m e n t
*
*****************************************
*
* Functional Description
* release request for an sql statement
*
*****************************************/
YEntry status(user_status);
try
{
Statement* statement = translate<Statement>(stmt_handle);
#ifndef NO_LOCAL_DSQL
if (statement->flags & HANDLE_STATEMENT_local)
dsql8_free_statement(status, &statement->handle, option);
else
#endif
CALL(PROC_DSQL_FREE, statement->implementation) (status,
&statement->handle,
option);
if (status[1])
{
return status[1];
}
if (option & DSQL_drop)
{
release_dsql_support(statement->das);
delete statement;
*stmt_handle = 0;
}
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_DSQL_INSERT(ISC_STATUS* user_status,
FB_API_HANDLE* stmt_handle,
USHORT dialect,
XSQLDA* sqlda)
{
/**************************************
*
* i s c _ d s q l _ i n s e r t
*
**************************************
*
* Functional description
* Insert next record into a dynamic SQL cursor
*
**************************************/
Status status(user_status);
try
{
Statement* statement = translate<Statement>(stmt_handle);
statement->checkPrepared();
sqlda_sup& dasup = statement->das;
USHORT blr_length, msg_type, msg_length;
if (UTLD_parse_sqlda(status, &dasup, &blr_length, &msg_type, &msg_length,
dialect, sqlda, DASUP_CLAUSE_bind))
{
return status[1];
}
return GDS_DSQL_INSERT_M(status, stmt_handle, blr_length,
dasup.dasup_clauses[DASUP_CLAUSE_bind].
dasup_blr, 0, msg_length,
dasup.dasup_clauses[DASUP_CLAUSE_bind].
dasup_msg);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_DSQL_INSERT_M(ISC_STATUS* user_status,
FB_API_HANDLE* stmt_handle,
USHORT blr_length,
const SCHAR* blr,
USHORT msg_type,
USHORT msg_length,
const SCHAR* msg)
{
/**************************************
*
* i s c _ d s q l _ i n s e r t _ m
*
**************************************
*
* Functional description
* Insert next record into a dynamic SQL cursor
*
**************************************/
YEntry status(user_status);
try
{
Statement* statement = translate<Statement>(stmt_handle);
status.setPrimaryHandle(statement);
statement->checkPrepared();
sqlda_sup& dasup = statement->das;
#ifndef NO_LOCAL_DSQL
if (statement->flags & HANDLE_STATEMENT_local)
dsql8_insert(status, &statement->handle,
blr_length, blr, msg_type, msg_length, msg);
else
#endif
CALL(PROC_DSQL_INSERT, statement->implementation) (status,
&statement->handle,
blr_length, blr,
msg_type,
msg_length,
msg);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_DSQL_PREPARE(ISC_STATUS* user_status,
FB_API_HANDLE* tra_handle,
FB_API_HANDLE* stmt_handle,
USHORT length,
const SCHAR* string,
USHORT dialect,
XSQLDA* sqlda)
{
/**************************************
*
* i s c _ d s q l _ p r e p a r e
*
**************************************
*
* Functional description
* Prepare a statement for execution.
*
**************************************/
Status status(user_status);
try
{
Statement* statement = translate<Statement>(stmt_handle);
USHORT buffer_len;
SCHAR *buffer;
sqlda_sup& dasup = statement->das;
buffer_len = sqlda_buffer_size(PREPARE_BUFFER_SIZE, sqlda, dialect);
Attachment* database = statement->parent;
buffer = database->db_prepare_buffer.getBuffer(buffer_len);
if (!GDS_DSQL_PREPARE_M(status,
tra_handle,
stmt_handle,
length,
string,
dialect,
sizeof(sql_prepare_info2),
sql_prepare_info2,
buffer_len,
buffer))
{
statement->flags &= ~HANDLE_STATEMENT_prepared;
release_dsql_support(dasup);
memset(&dasup, 0, sizeof(dasup));
dasup.dasup_dialect = dialect;
SCHAR* p = buffer;
dasup.dasup_stmt_type = 0;
if (*p == isc_info_sql_stmt_type)
{
const USHORT len = gds__vax_integer((UCHAR*)p + 1, 2);
dasup.dasup_stmt_type = gds__vax_integer((UCHAR*)p + 3, len);
p += 3 + len;
}
sqlda_sup::dasup_clause &das_select = dasup.dasup_clauses[DASUP_CLAUSE_select];
sqlda_sup::dasup_clause &das_bind = dasup.dasup_clauses[DASUP_CLAUSE_bind];
das_select.dasup_info_buf = das_bind.dasup_info_buf = 0;
das_select.dasup_info_len = das_bind.dasup_info_len = 0;
if (*p == isc_info_sql_select)
das_select.dasup_info_buf = p;
das_bind.dasup_info_buf = UTLD_skip_sql_info(p);
p = das_select.dasup_info_buf;
if (p)
{
SCHAR* p2 = das_bind.dasup_info_buf;
if (p2)
{
const SLONG len = p2 - p;
p2 = alloc(len + 1);
memmove(p2, p, len);
p2[len] = isc_info_end;
das_select.dasup_info_buf = p2;
das_select.dasup_info_len = len + 1;
}
else
{
das_select.dasup_info_buf = 0;
das_select.dasup_info_len = 0;
}
}
p = das_bind.dasup_info_buf;
if (p)
{
SCHAR* p2 = UTLD_skip_sql_info(p);
if (p2)
{
const SLONG len = p2 - p;
p2 = alloc(len + 1);
memmove(p2, p, len);
p2[len] = isc_info_end;
das_bind.dasup_info_buf = p2;
das_bind.dasup_info_len = len + 1;
}
else
{
das_bind.dasup_info_buf = 0;
das_bind.dasup_info_len = 0;
}
}
iterative_sql_info(status, stmt_handle, sizeof(sql_prepare_info),
sql_prepare_info, // buffer_len, buffer, dialect,
das_select.dasup_info_len,
das_select.dasup_info_buf,
dialect, sqlda);
// statement prepared OK
statement->flags |= HANDLE_STATEMENT_prepared;
}
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_DSQL_PREPARE_M(ISC_STATUS* user_status,
FB_API_HANDLE* tra_handle,
FB_API_HANDLE* stmt_handle,
USHORT length,
const SCHAR* string,
USHORT dialect,
USHORT item_length,
const SCHAR* items,
USHORT buffer_length,
SCHAR* buffer)
{
/**************************************
*
* i s c _ d s q l _ p r e p a r e _ m
*
**************************************
*
* Functional description
* Prepare a statement for execution.
*
**************************************/
YEntry status(user_status);
try
{
Statement* statement = translate<Statement>(stmt_handle);
status.setPrimaryHandle(statement);
StTra *handle = 0;
if (*tra_handle)
{
Transaction* transaction = translate<Transaction>(tra_handle);
transaction = find_transaction(statement->parent, transaction);
if (!transaction)
{
bad_handle (isc_bad_trans_handle);
}
handle = transaction->handle;
}
#ifndef NO_LOCAL_DSQL
if (statement->flags & HANDLE_STATEMENT_local)
{
dsql8_prepare(status, tra_handle, &statement->handle,
length, string, dialect, item_length, items,
buffer_length, buffer);
}
else
#endif
{
CALL(PROC_DSQL_PREPARE, statement->implementation) (status,
&handle,
&statement->handle,
length,
string, dialect,
item_length,
items,
buffer_length,
buffer);
}
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_DSQL_SET_CURSOR(ISC_STATUS* user_status,
FB_API_HANDLE* stmt_handle,
const SCHAR* cursor,
USHORT type)
{
/**************************************
*
* i s c _ d s q l _ s e t _ c u r s o r
*
**************************************
*
* Functional description
* Set a cursor name for a dynamic request.
*
**************************************/
YEntry status(user_status);
try
{
Statement* statement = translate<Statement>(stmt_handle);
status.setPrimaryHandle(statement);
#ifndef NO_LOCAL_DSQL
if (statement->flags & HANDLE_STATEMENT_local)
dsql8_set_cursor(status, &statement->handle, cursor, type);
else
#endif
CALL(PROC_DSQL_SET_CURSOR, statement->implementation) (status,
&statement->
handle, cursor,
type);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_DSQL_SQL_INFO(ISC_STATUS* user_status,
FB_API_HANDLE* stmt_handle,
SSHORT item_length,
const SCHAR* items,
SSHORT buffer_length,
SCHAR* buffer)
{
/**************************************
*
* i s c _ d s q l _ s q l _ i n f o
*
**************************************
*
* Functional description
* Provide information on sql statement.
*
**************************************/
YEntry status(user_status);
try
{
Statement* statement = translate<Statement>(stmt_handle);
status.setPrimaryHandle(statement);
#ifndef NO_LOCAL_DSQL
if (statement->flags & HANDLE_STATEMENT_local)
dsql8_sql_info(status, &statement->handle, item_length, items,
buffer_length, buffer);
else
#endif
{
if (( (item_length == 1) && (items[0] == isc_info_sql_stmt_type) ||
(item_length == 2) && (items[0] == isc_info_sql_stmt_type) &&
(items[1] == isc_info_end || items[1] == 0) ) &&
(statement->flags & HANDLE_STATEMENT_prepared) &&
statement->das.dasup_stmt_type)
{
*buffer++ = isc_info_sql_stmt_type;
put_short((UCHAR*) buffer, 4);
buffer += 2;
put_long((UCHAR*) buffer, statement->das.dasup_stmt_type);
}
else
{
CALL(PROC_DSQL_SQL_INFO, statement->implementation) (status,
&statement->handle,
item_length,
items,
buffer_length,
buffer);
}
}
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
int API_ROUTINE gds__enable_subsystem(TEXT * subsystem)
{
/**************************************
*
* g d s _ $ e n a b l e _ s u b s y s t e m
*
**************************************
*
* Functional description
* Enable access to a specific subsystem. If no subsystem
* has been explicitly enabled, all are available.
*
**************************************/
const IMAGE *sys, *end;
for (sys = images, end = sys + SUBSYSTEMS; sys < end; sys++)
if (!strcmp(sys->name, subsystem)) {
if (!~why_enabled)
why_enabled = 0;
why_enabled |= (1 << (sys - images));
return TRUE;
}
return FALSE;
}
#ifndef REQUESTER
ISC_STATUS API_ROUTINE isc_wait_for_event(ISC_STATUS * user_status,
FB_API_HANDLE * handle,
USHORT length,
const UCHAR* events,
UCHAR* buffer)
{
/**************************************
*
* g d s _ $ e v e n t _ w a i t
*
**************************************
*
* Functional description
* Que request for event notification.
*
**************************************/
Status status(user_status);
try
{
if (!why_initialized)
{
gds__register_cleanup((FPTR_VOID_PTR) exit_handler, why_event);
why_initialized = TRUE;
ISC_event_init(why_event, 0, 0);
}
SLONG value = ISC_event_clear(why_event);
SLONG id;
if (GDS_QUE_EVENTS
(status, handle, &id, length, events, event_ast, buffer))
{
return status[1];
}
event_t* event_ptr = why_event;
ISC_event_wait(1, &event_ptr, &value, -1, 0, NULL);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
#endif
ISC_STATUS API_ROUTINE GDS_INTL_FUNCTION(ISC_STATUS * user_status,
FB_API_HANDLE * handle,
USHORT function,
UCHAR charSetNumber,
USHORT strLen,
const UCHAR* str,
USHORT* result)
{
/**************************************
*
* g d s _ i n t l _ f u n c t i o n
*
**************************************
*
* Functional description
* Return INTL informations.
* (candidate for removal when engine functions can be called by DSQL)
*
**************************************/
YEntry status(user_status);
try
{
Attachment* dbb = translate<Attachment>(handle);
status.setPrimaryHandle(dbb);
CALL(PROC_INTL_FUNCTION, dbb->implementation) (status,
&dbb->handle,
function, charSetNumber,
strLen, str, result);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_DSQL_CACHE(ISC_STATUS * user_status,
FB_API_HANDLE * handle,
USHORT operation,
int type,
const char* name,
bool* result)
{
YEntry status(user_status);
try
{
Attachment* dbb = translate<Attachment>(handle);
status.setPrimaryHandle(dbb);
CALL(PROC_DSQL_CACHE, dbb->implementation) (status, &dbb->handle,
operation, type,
name, result);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_INTERNAL_COMPILE(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* req_handle,
USHORT blr_length,
const SCHAR* blr,
USHORT string_length,
const char* string,
USHORT dbginfo_length,
const UCHAR* dbginfo)
{
YEntry status(user_status);
Attachment* dbb = 0;
StReq* rq = 0;
try
{
dbb = translate<Attachment>(db_handle);
status.setPrimaryHandle(dbb);
nullCheck(req_handle, isc_bad_req_handle);
if (CALL(PROC_INTERNAL_COMPILE, dbb->implementation) (status, &dbb->handle,
&rq, blr_length,
blr,
string_length, string,
dbginfo_length, dbginfo))
{
return status[1];
}
new Request(rq, req_handle, dbb);
}
catch (const Firebird::Exception& e)
{
*req_handle = 0;
if (dbb && rq)
{
CALL(PROC_RELEASE_REQUEST, dbb->implementation) (status, rq);
}
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_GET_SEGMENT(ISC_STATUS * user_status,
FB_API_HANDLE * blob_handle,
USHORT * length,
USHORT buffer_length,
UCHAR * buffer)
{
/**************************************
*
* g d s _ $ g e t _ s e g m e n t
*
**************************************
*
* Functional description
* Abort a partially completed blob.
*
**************************************/
YEntry status(user_status);
try
{
Blob* blob = translate<Blob>(blob_handle);
status.setPrimaryHandle(blob);
ISC_STATUS code =
CALL(PROC_GET_SEGMENT, blob->implementation) (status, &blob->handle,
length,
buffer_length, buffer);
if (code == isc_segstr_eof || code == isc_segment)
{
status.ok();
return code;
}
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_GET_SLICE(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
ISC_QUAD* array_id,
USHORT sdl_length,
const UCHAR* sdl,
USHORT param_length,
const UCHAR* param,
SLONG slice_length,
UCHAR* slice,
SLONG* return_length)
{
/**************************************
*
* g d s _ $ g e t _ s l i c e
*
**************************************
*
* Functional description
* Snatch a slice of an array.
*
**************************************/
YEntry status(user_status);
try
{
Attachment* dbb = translate<Attachment>(db_handle);
status.setPrimaryHandle(dbb);
Transaction* transaction = findTransaction(tra_handle, dbb);
CALL(PROC_GET_SLICE, dbb->implementation) (status,
&dbb->handle,
&transaction->handle,
array_id,
sdl_length, sdl,
param_length, param,
slice_length, slice,
return_length);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS gds__handle_cleanup(ISC_STATUS * user_status,
FB_API_HANDLE * user_handle)
{
/**************************************
*
* g d s _ $ h a n d l e _ c l e a n u p
*
**************************************
*
* Functional description
* Clean up a dangling y-valve handle.
*
**************************************/
Status status(user_status);
try
{
Transaction* transaction = translate<Transaction>(user_handle);
while (transaction) {
Transaction* sub = transaction;
transaction = sub->next;
delete sub;
}
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_OPEN_BLOB(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
FB_API_HANDLE* blob_handle,
SLONG* blob_id)
{
/**************************************
*
* g d s _ $ o p e n _ b l o b
*
**************************************
*
* Functional description
* Open an existing blob.
*
**************************************/
return open_blob(user_status, db_handle, tra_handle, blob_handle, blob_id,
0, 0, PROC_OPEN_BLOB, PROC_OPEN_BLOB2);
}
ISC_STATUS API_ROUTINE GDS_OPEN_BLOB2(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
FB_API_HANDLE* blob_handle,
SLONG* blob_id,
SSHORT bpb_length,
const UCHAR* bpb)
{
/**************************************
*
* g d s _ $ o p e n _ b l o b 2
*
**************************************
*
* Functional description
* Open an existing blob (extended edition).
*
**************************************/
return open_blob(user_status, db_handle, tra_handle, blob_handle, blob_id,
bpb_length, bpb, PROC_OPEN_BLOB,
PROC_OPEN_BLOB2);
}
ISC_STATUS API_ROUTINE GDS_PREPARE(ISC_STATUS* user_status,
FB_API_HANDLE* tra_handle)
{
/**************************************
*
* g d s _ $ p r e p a r e
*
**************************************
*
* Functional description
* Prepare a transaction for commit. First phase of a two
* phase commit.
*
**************************************/
return GDS_PREPARE2(user_status, tra_handle, 0, 0);
}
ISC_STATUS API_ROUTINE GDS_PREPARE2(ISC_STATUS* user_status,
FB_API_HANDLE* tra_handle,
USHORT msg_length,
const UCHAR* msg)
{
/**************************************
*
* g d s _ $ p r e p a r e 2
*
**************************************
*
* Functional description
* Prepare a transaction for commit. First phase of a two
* phase commit.
*
**************************************/
YEntry status(user_status);
try
{
Transaction* transaction = translate<Transaction>(tra_handle);
status.setPrimaryHandle(transaction);
for (Transaction* sub = transaction; sub; sub = sub->next)
{
if (sub->implementation != SUBSYSTEMS &&
CALL(PROC_PREPARE, sub->implementation) (status, &sub->handle,
msg_length, msg))
{
return status[1];
}
}
transaction->flags |= HANDLE_TRANSACTION_limbo;
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_PUT_SEGMENT(ISC_STATUS* user_status,
FB_API_HANDLE* blob_handle,
USHORT buffer_length,
const UCHAR* buffer)
{
/**************************************
*
* g d s _ $ p u t _ s e g m e n t
*
**************************************
*
* Functional description
* Abort a partially completed blob.
*
**************************************/
YEntry status(user_status);
try
{
Blob* blob = translate<Blob>(blob_handle);
status.setPrimaryHandle(blob);
CALL(PROC_PUT_SEGMENT, blob->implementation) (status,
&blob->handle,
buffer_length,
buffer);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_PUT_SLICE(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
ISC_QUAD* array_id,
USHORT sdl_length,
const UCHAR* sdl,
USHORT param_length,
const SLONG* param,
SLONG slice_length,
UCHAR* slice)
{
/**************************************
*
* g d s _ $ p u t _ s l i c e
*
**************************************
*
* Functional description
* Snatch a slice of an array.
*
**************************************/
YEntry status(user_status);
try
{
Attachment* dbb = translate<Attachment>(db_handle);
status.setPrimaryHandle(dbb);
Transaction* transaction = findTransaction(tra_handle, dbb);
CALL(PROC_PUT_SLICE, dbb->implementation) (status,
&dbb->handle,
&transaction->handle,
array_id,
sdl_length,
sdl,
param_length,
param,
slice_length,
slice);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_QUE_EVENTS(ISC_STATUS* user_status,
FB_API_HANDLE* handle,
SLONG* id,
USHORT length,
const UCHAR* events,
FPTR_EVENT_CALLBACK ast,
void* arg)
{
/**************************************
*
* g d s _ $ q u e _ e v e n t s
*
**************************************
*
* Functional description
* Que request for event notification.
*
**************************************/
YEntry status(user_status);
try
{
Attachment* dbb = translate<Attachment>(handle);
status.setPrimaryHandle(dbb);
CALL(PROC_QUE_EVENTS, dbb->implementation) (status, &dbb->handle,
id, length, events,
ast, arg);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_RECEIVE(ISC_STATUS* user_status,
FB_API_HANDLE* req_handle,
USHORT msg_type,
USHORT msg_length,
SCHAR* msg,
SSHORT level)
{
/**************************************
*
* g d s _ $ r e c e i v e
*
**************************************
*
* Functional description
* Get a record from the host program.
*
**************************************/
#ifdef SCROLLABLE_CURSORS
return GDS_RECEIVE2(user_status, req_handle, msg_type, msg_length,
msg, level, (USHORT) blr_continue, /* means continue in same direction as before */
(ULONG) 1);
#else
YEntry status(user_status);
try
{
Request* request = translate<Request>(req_handle);
status.setPrimaryHandle(request);
CALL(PROC_RECEIVE, request->implementation) (status,
&request->handle,
msg_type,
msg_length,
msg,
level);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
#endif
}
#ifdef SCROLLABLE_CURSORS
ISC_STATUS API_ROUTINE GDS_RECEIVE2(ISC_STATUS* user_status,
FB_API_HANDLE* req_handle,
USHORT msg_type,
USHORT msg_length,
SCHAR* msg,
SSHORT level,
USHORT direction,
ULONG offset)
{
/**************************************
*
* i s c _ r e c e i v e 2
*
**************************************
*
* Functional description
* Scroll through the request output stream,
* then get a record from the host program.
*
**************************************/
YEntry status(user_status);
try
{
Request* request = translate<Request>(req_handle);
status.setPrimaryHandle(request);
CALL(PROC_RECEIVE, request->implementation) (status,
&request->handle,
msg_type,
msg_length,
msg,
level,
direction,
offset);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
#endif
ISC_STATUS API_ROUTINE GDS_RECONNECT(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
SSHORT length,
const UCHAR* id)
{
/**************************************
*
* g d s _ $ r e c o n n e c t
*
**************************************
*
* Functional description
* Connect to a transaction in limbo.
*
**************************************/
YEntry status(user_status);
StTra* handle = 0;
try
{
nullCheck(tra_handle, isc_bad_trans_handle);
Attachment* dbb = translate<Attachment>(db_handle);
status.setPrimaryHandle(dbb);
if (CALL(PROC_RECONNECT, dbb->implementation) (status, &dbb->handle,
&handle,
length, id))
{
return status[1];
}
new Transaction(handle, tra_handle, dbb);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
if (handle)
{
*tra_handle = 0;
}
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_RELEASE_REQUEST(ISC_STATUS * user_status,
FB_API_HANDLE * req_handle)
{
/**************************************
*
* g d s _ $ r e l e a s e _ r e q u e s t
*
**************************************
*
* Functional description
* Release a request.
*
**************************************/
YEntry status(user_status);
try
{
Request* request = translate<Request>(req_handle);
status.setPrimaryHandle(request);
if (!CALL(PROC_RELEASE_REQUEST, request->implementation) (status,
&request->handle))
{
delete request;
*req_handle = 0;
}
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_REQUEST_INFO(ISC_STATUS* user_status,
FB_API_HANDLE* req_handle,
SSHORT level,
SSHORT item_length,
const SCHAR* items,
SSHORT buffer_length,
SCHAR* buffer)
{
/**************************************
*
* g d s _ $ r e q u e s t _ i n f o
*
**************************************
*
* Functional description
* Provide information on blob object.
*
**************************************/
YEntry status(user_status);
try
{
Request* request = translate<Request>(req_handle);
status.setPrimaryHandle(request);
CALL(PROC_REQUEST_INFO, request->implementation) (status,
&request->handle,
level,
item_length,
items,
buffer_length,
buffer);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
#if defined (SOLARIS) || defined (WIN_NT)
extern "C"
#endif
SLONG API_ROUTINE isc_reset_fpe(USHORT fpe_status)
{
/**************************************
*
* i s c _ r e s e t _ f p e
*
**************************************
*
* Functional description
* API to be used to tell InterBase to reset it's
* FPE handler - eg: client has an FPE of it's own
* and just changed it.
*
* Returns
* Prior setting of the FPE reset flag
*
**************************************/
#if !(defined REQUESTER || defined SUPERCLIENT || defined SUPERSERVER)
SLONG prior;
prior = (SLONG) subsystem_FPE_reset;
switch (fpe_status) {
case FPE_RESET_INIT_ONLY:
subsystem_FPE_reset = fpe_status;
break;
case FPE_RESET_NEXT_API_CALL:
subsystem_FPE_reset = fpe_status;
break;
case FPE_RESET_ALL_API_CALL:
subsystem_FPE_reset = fpe_status;
break;
default:
break;
}
return prior;
#else
return FPE_RESET_INIT_ONLY;
#endif
}
ISC_STATUS API_ROUTINE GDS_ROLLBACK_RETAINING(ISC_STATUS * user_status,
FB_API_HANDLE * tra_handle)
{
/**************************************
*
* i s c _ r o l l b a c k _ r e t a i n i n g
*
**************************************
*
* Functional description
* Abort a transaction, but keep all cursors open.
*
**************************************/
YEntry status(user_status);
try
{
Transaction* transaction = translate<Transaction>(tra_handle);
status.setPrimaryHandle(transaction);
for (Transaction* sub = transaction; sub; sub = sub->next)
{
if (sub->implementation != SUBSYSTEMS &&
CALL(PROC_ROLLBACK_RETAINING, sub->implementation) (status,
&sub->handle))
{
return status[1];
}
}
transaction->flags |= HANDLE_TRANSACTION_limbo;
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_ROLLBACK(ISC_STATUS * user_status,
FB_API_HANDLE * tra_handle)
{
/**************************************
*
* g d s _ $ r o l l b a c k
*
**************************************
*
* Functional description
* Abort a transaction.
*
**************************************/
YEntry status(user_status);
try
{
Transaction* transaction = translate<Transaction>(tra_handle);
status.setPrimaryHandle(transaction);
for (Transaction* sub = transaction; sub; sub = sub->next)
if (sub->implementation != SUBSYSTEMS &&
CALL(PROC_ROLLBACK, sub->implementation) (status, &sub->handle))
{
if (!is_network_error(status) ||
(transaction->flags & HANDLE_TRANSACTION_limbo) )
{
return status[1];
}
}
if (is_network_error(status))
{
init_status(status);
}
while (transaction)
{
Transaction* sub = transaction;
transaction = sub->next;
delete sub;
}
*tra_handle = 0;
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_SEEK_BLOB(ISC_STATUS * user_status,
FB_API_HANDLE * blob_handle,
SSHORT mode,
SLONG offset,
SLONG * result)
{
/**************************************
*
* g d s _ $ s e e k _ b l o b
*
**************************************
*
* Functional description
* Seek a blob.
*
**************************************/
YEntry status(user_status);
try
{
Blob* blob = translate<Blob>(blob_handle);
status.setPrimaryHandle(blob);
/***
if (blob->flags & HANDLE_BLOB_filter)
{
subsystem_exit();
BLF_close_blob (status, &blob->handle);
subsystem_enter();
}
else
***/
CALL(PROC_SEEK_BLOB, blob->implementation) (status,
&blob->handle,
mode,
offset, result);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_SEND(ISC_STATUS* user_status,
FB_API_HANDLE* req_handle,
USHORT msg_type,
USHORT msg_length,
const SCHAR* msg,
SSHORT level)
{
/**************************************
*
* g d s _ $ s e n d
*
**************************************
*
* Functional description
* Get a record from the host program.
*
**************************************/
YEntry status(user_status);
try
{
Request* request = translate<Request>(req_handle);
status.setPrimaryHandle(request);
CALL(PROC_SEND, request->implementation) (status, &request->handle,
msg_type, msg_length, msg,
level);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_SERVICE_ATTACH(ISC_STATUS* user_status,
USHORT service_length,
const TEXT* service_name,
FB_API_HANDLE* public_handle,
USHORT spb_length,
const SCHAR* spb)
{
/**************************************
*
* i s c _ s e r v i c e _ a t t a c h
*
**************************************
*
* Functional description
* Attach a service through the first subsystem
* that recognizes it.
*
**************************************/
StSvc* handle = 0;
Service* service = 0;
ISC_STATUS_ARRAY temp;
USHORT n;
YEntry status(user_status);
try
{
nullCheck(public_handle, isc_bad_svc_handle);
if (!service_name)
{
Firebird::status_exception::raise(isc_service_att_err,
isc_arg_gds, isc_svc_name_missing, isc_arg_end);
}
if (spb_length > 0 && !spb)
{
Firebird::status_exception::raise(isc_bad_spb_form, isc_arg_end);
}
#if !defined (SUPERCLIENT) && !defined (REQUESTER)
if (shutdown_flag)
{
Firebird::status_exception::raise(isc_shutwarn, isc_arg_end);
}
#endif /* !SUPERCLIENT && !REQUESTER */
SUBSYSTEM_USAGE_INCR;
USHORT org_length = service_length;
if (org_length) {
const TEXT* p = service_name + org_length - 1;
while (*p == ' ')
p--;
org_length = p - service_name + 1;
}
ISC_STATUS* ptr = status;
for (n = 0; n < SUBSYSTEMS; n++)
{
if (why_enabled && !(why_enabled & (1 << n)))
{
continue;
}
if (!CALL(PROC_SERVICE_ATTACH, n) (ptr,
org_length, service_name,
&handle,
spb_length, spb))
{
service = new Service(handle, public_handle, n);
status[0] = isc_arg_gds;
status[1] = 0;
if (status[2] != isc_arg_warning)
{
status[2] = isc_arg_end;
}
return status[1];
}
if (ptr[1] != isc_unavailable)
{
ptr = temp;
}
}
SUBSYSTEM_USAGE_DECR;
if (status[1] == isc_unavailable)
{
status[1] = isc_service_att_err;
}
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
if (handle)
{
CALL(PROC_SERVICE_DETACH, n) (temp, handle);
*public_handle = 0;
delete service;
}
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_SERVICE_DETACH(ISC_STATUS * user_status,
FB_API_HANDLE * handle)
{
/**************************************
*
* i s c _ s e r v i c e _ d e t a c h
*
**************************************
*
* Functional description
* Close down a service.
*
**************************************/
YEntry status(user_status);
try
{
Service* service = translate<Service>(handle);
if (CALL(PROC_SERVICE_DETACH, service->implementation) (status,
&service->handle))
{
return status[1];
}
SUBSYSTEM_USAGE_DECR;
delete service;
*handle = 0;
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_SERVICE_QUERY(ISC_STATUS* user_status,
FB_API_HANDLE* handle,
ULONG* reserved,
USHORT send_item_length,
const SCHAR* send_items,
USHORT recv_item_length,
const SCHAR* recv_items,
USHORT buffer_length,
SCHAR* buffer)
{
/**************************************
*
* i s c _ s e r v i c e _ q u e r y
*
**************************************
*
* Functional description
* Provide information on service object.
*
* NOTE: The parameter RESERVED must not be used
* for any purpose as there are networking issues
* involved (as with any handle that goes over the
* network). This parameter will be implemented at
* a later date.
**************************************/
YEntry status(user_status);
try
{
Service* service = translate<Service>(handle);
CALL(PROC_SERVICE_QUERY, service->implementation) (status,
&service->handle,
0, /* reserved */
send_item_length,
send_items,
recv_item_length,
recv_items,
buffer_length,
buffer);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_SERVICE_START(ISC_STATUS* user_status,
FB_API_HANDLE* handle,
ULONG* reserved,
USHORT spb_length,
const SCHAR* spb)
{
/**************************************
*
* i s c _ s e r v i c e _ s t a r t
*
**************************************
*
* Functional description
* Starts a service thread
*
* NOTE: The parameter RESERVED must not be used
* for any purpose as there are networking issues
* involved (as with any handle that goes over the
* network). This parameter will be implemented at
* a later date.
**************************************/
YEntry status(user_status);
try
{
Service* service = translate<Service>(handle);
CALL(PROC_SERVICE_START, service->implementation) (status,
&service->handle,
NULL,
spb_length, spb);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_START_AND_SEND(ISC_STATUS* user_status,
FB_API_HANDLE* req_handle,
FB_API_HANDLE* tra_handle,
USHORT msg_type,
USHORT msg_length,
const SCHAR* msg,
SSHORT level)
{
/**************************************
*
* g d s _ $ s t a r t _ a n d _ s e n d
*
**************************************
*
* Functional description
* Get a record from the host program.
*
**************************************/
YEntry status(user_status);
try
{
Request* request = translate<Request>(req_handle);
status.setPrimaryHandle(request);
Transaction* transaction = findTransaction(tra_handle, request->parent);
CALL(PROC_START_AND_SEND, request->implementation) (status,
&request->handle,
&transaction->
handle, msg_type,
msg_length, msg,
level);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_START(ISC_STATUS * user_status,
FB_API_HANDLE * req_handle,
FB_API_HANDLE * tra_handle,
SSHORT level)
{
/**************************************
*
* g d s _ $ s t a r t
*
**************************************
*
* Functional description
* Get a record from the host program.
*
**************************************/
YEntry status(user_status);
try
{
Request* request = translate<Request>(req_handle);
status.setPrimaryHandle(request);
Transaction* transaction = findTransaction(tra_handle, request->parent);
CALL(PROC_START, request->implementation) (status,
&request->handle,
&transaction->handle,
level);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_START_MULTIPLE(ISC_STATUS * user_status,
FB_API_HANDLE * tra_handle,
SSHORT count,
// TEB * vector)
void* vec)
{
/**************************************
*
* g d s _ $ s t a r t _ m u l t i p l e
*
**************************************
*
* Functional description
* Start a transaction.
*
**************************************/
TEB* vector = (TEB*) vec;
ISC_STATUS_ARRAY temp;
Transaction* transaction = 0;
Attachment* dbb = 0;
StTra* handle = 0;
YEntry status(user_status);
try
{
nullCheck(tra_handle, isc_bad_trans_handle);
if (count <= 0)
{
Firebird::status_exception::raise(isc_bad_trans_handle,
/* Do we need new error code here ? */ isc_arg_end);
}
Transaction** ptr;
USHORT n;
for (n = 0, ptr = &transaction; n < count;
n++, ptr = &(*ptr)->next, vector++)
{
dbb = translate<Attachment>(vector->teb_database);
if (CALL(PROC_START_TRANSACTION, dbb->implementation) (status,
&handle,
1,
&dbb->handle,
vector->teb_tpb_length,
vector->teb_tpb))
{
Firebird::status_exception::raise(status);
}
*ptr = new Transaction(handle, 0, dbb);
handle = 0;
}
if (transaction->next)
{
Transaction *sub = new Transaction(tra_handle, SUBSYSTEMS);
sub->next = transaction;
}
else {
*tra_handle = transaction->public_handle;
}
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
if (handle || transaction)
{
*tra_handle = 0;
}
while (transaction)
{
Transaction *sub = transaction;
transaction = sub->next;
if (sub->handle)
{
CALL(PROC_ROLLBACK, sub->implementation) (temp, &sub->handle);
}
delete sub;
}
if (handle && dbb)
{
CALL(PROC_ROLLBACK, dbb->implementation) (temp, handle);
}
}
return status[1];
}
ISC_STATUS API_ROUTINE_VARARG GDS_START_TRANSACTION(ISC_STATUS * user_status,
FB_API_HANDLE * tra_handle,
SSHORT count, ...)
{
/**************************************
*
* g d s _ $ s t a r t _ t r a n s a c t i o n
*
**************************************
*
* Functional description
* Start a transaction.
*
**************************************/
Status status(user_status);
try
{
Firebird::HalfStaticArray<TEB, 16> tebs;
TEB* teb = tebs.getBuffer(count);
const TEB* const end = teb + count;
va_list ptr;
va_start(ptr, count);
for (TEB* teb_iter = teb; teb_iter < end; teb_iter++) {
teb_iter->teb_database = va_arg(ptr, FB_API_HANDLE*);
teb_iter->teb_tpb_length = va_arg(ptr, int);
teb_iter->teb_tpb = va_arg(ptr, UCHAR *);
}
va_end(ptr);
GDS_START_MULTIPLE(user_status, tra_handle, count, teb);
status.ok();
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_TRANSACT_REQUEST(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
USHORT blr_length,
const SCHAR* blr,
USHORT in_msg_length,
SCHAR* in_msg,
USHORT out_msg_length,
SCHAR* out_msg)
{
/**************************************
*
* i s c _ t r a n s a c t _ r e q u e s t
*
**************************************
*
* Functional description
* Execute a procedure.
*
**************************************/
YEntry status(user_status);
try
{
Attachment* dbb = translate<Attachment>(db_handle);
status.setPrimaryHandle(dbb);
Transaction* transaction = findTransaction(tra_handle, dbb);
CALL(PROC_TRANSACT_REQUEST, dbb->implementation) (status,
&dbb->handle,
&transaction->
handle, blr_length,
blr, in_msg_length,
in_msg,
out_msg_length,
out_msg);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE gds__transaction_cleanup(ISC_STATUS * user_status,
FB_API_HANDLE * tra_handle,
TransactionCleanupRoutine *routine,
void* arg)
{
/**************************************
*
* g d s _ $ t r a n s a c t i o n _ c l e a n u p
*
**************************************
*
* Functional description
* Register a transaction specific cleanup handler.
*
**************************************/
Status status(user_status);
try
{
translate<Transaction>(tra_handle)->cleanup.add(routine, arg);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_TRANSACTION_INFO(ISC_STATUS* user_status,
FB_API_HANDLE* tra_handle,
SSHORT item_length,
const SCHAR* items,
SSHORT buffer_length,
UCHAR* buffer)
{
/**************************************
*
* g d s _ $ t r a n s a c t i o n _ i n f o
*
**************************************
*
* Functional description
* Provide information on transaction object.
*
**************************************/
YEntry status(user_status);
try
{
Transaction* transaction = translate<Transaction>(tra_handle);
status.setPrimaryHandle(transaction);
if (transaction->implementation != SUBSYSTEMS) {
CALL(PROC_TRANSACTION_INFO, transaction->implementation) (status,
&transaction->
handle,
item_length,
items,
buffer_length,
buffer);
}
else {
SSHORT item_len = item_length;
SSHORT buffer_len = buffer_length;
for (Transaction* sub = transaction->next; sub; sub = sub->next) {
if (CALL(PROC_TRANSACTION_INFO, sub->implementation) (status,
&sub->
handle,
item_len,
items,
buffer_len,
buffer))
{
return status[1];
}
UCHAR* ptr = buffer;
const UCHAR* const end = buffer + buffer_len;
while (ptr < end && *ptr == isc_info_tra_id)
{
ptr += 3 + gds__vax_integer(ptr + 1, 2);
}
if (ptr >= end || *ptr != isc_info_end)
{
return status[1];
}
buffer_len = end - ptr;
buffer = ptr;
}
}
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
ISC_STATUS API_ROUTINE GDS_UNWIND(ISC_STATUS * user_status,
FB_API_HANDLE * req_handle,
SSHORT level)
{
/**************************************
*
* g d s _ $ u n w i n d
*
**************************************
*
* Functional description
* Unwind a running request. This is potentially nasty since it can be called
* asynchronously.
*
**************************************/
YEntry status(user_status);
try
{
Request* request = translate<Request>(req_handle);
status.setPrimaryHandle(request);
CALL(PROC_UNWIND, request->implementation) (status,
&request->handle,
level);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
#ifdef DEBUG_GDS_ALLOC
static SCHAR *alloc_debug(SLONG length, const char* file, int line)
#else
static SCHAR *alloc(SLONG length)
#endif
{
/**************************************
*
* a l l o c
*
**************************************
*
* Functional description
* Allocate some memory.
*
**************************************/
SCHAR *block;
#ifdef DEBUG_GDS_ALLOC
if (block = reinterpret_cast<SCHAR *>(gds__alloc_debug((SLONG) (sizeof(SCHAR) * length), file, line)))
#else
if (block = reinterpret_cast<SCHAR *>(gds__alloc((SLONG) (sizeof(SCHAR) * length))))
#endif
memset(block, 0, length);
else
Firebird::BadAlloc::raise();
return block;
}
static void bad_handle(ISC_STATUS code)
{
/**************************************
*
* b a d _ h a n d l e
*
**************************************
*
* Functional description
* Generate an error for a bad handle.
*
**************************************/
Firebird::status_exception::raise(code, isc_arg_end);
}
#ifdef DEV_BUILD
static void check_status_vector(const ISC_STATUS* status)
{
/**************************************
*
* c h e c k _ s t a t u s _ v e c t o r
*
**************************************
*
* Functional description
* Validate that a status vector looks valid.
*
**************************************/
#define SV_MSG(x) { gds__log ("%s %d check_status_vector: %s", __FILE__, __LINE__, (x)); BREAKPOINT (__LINE__); }
const ISC_STATUS* s = status;
if (!s) {
SV_MSG("Invalid status vector");
return;
}
if (*s != isc_arg_gds) {
SV_MSG("Must start with isc_arg_gds");
return;
}
/* Vector [2] could either end the vector, or start a warning
in either case the status vector is a success */
if ((s[1] == FB_SUCCESS) &&
(s[2] != isc_arg_end) &&
(s[2] != isc_arg_gds) &&
(s[2] != isc_arg_warning))
{
SV_MSG("Bad success vector format");
}
ULONG length;
while (*s != isc_arg_end) {
const ISC_STATUS code = *s++;
switch (code) {
case isc_arg_warning:
case isc_arg_gds:
/* The next element must either be 0 (indicating no error) or a
* valid isc error message, let's check */
if (*s && (*s & ISC_MASK) != ISC_MASK) {
if (code == isc_arg_warning) {
SV_MSG("warning code not a valid ISC message");
}
else {
SV_MSG("error code not a valid ISC message");
}
}
/* If the error code is valid, then I better be able to retrieve a
* proper facility code from it ... let's find out */
if (*s && (*s & ISC_MASK) == ISC_MASK) {
bool found = false;
const struct _facilities* facs = facilities;
const int fac_code = GET_FACILITY(*s);
while (facs->facility) {
if (facs->fac_code == fac_code) {
found = true;
break;
}
facs++;
}
if (!found)
if (code == isc_arg_warning) {
SV_MSG
("warning code does not contain a valid facility");
}
else {
SV_MSG
("error code does not contain a valid facility");
}
}
s++;
break;
case isc_arg_interpreted:
case isc_arg_string:
length = strlen((const char*) *s);
/* This check is heuristic, not deterministic */
if (length > 1024 - 1)
SV_MSG("suspect length value");
if (*((const UCHAR *) * s) == 0xCB)
SV_MSG("string in freed memory");
s++;
break;
case isc_arg_cstring:
length = (ULONG) * s;
s++;
/* This check is heuristic, not deterministic */
/* Note: This can never happen anyway, as the length is coming
from a byte value */
if (length > 1024 - 1)
SV_MSG("suspect length value");
if (*((const UCHAR *) * s) == 0xCB)
SV_MSG("string in freed memory");
s++;
break;
case isc_arg_number:
case isc_arg_vms:
case isc_arg_unix:
case isc_arg_win32:
s++;
break;
default:
SV_MSG("invalid status code");
return;
}
if ((s - status) >= ISC_STATUS_LENGTH)
SV_MSG("vector too long");
}
#undef SV_MSG
}
#endif
// Make this repetitive block a function.
// Call all cleanup routines registered with the transaction.
/*void WHY_cleanup_transaction(Transaction* transaction)
{
for (clean* cln = transaction->cleanup; cln; cln = transaction->cleanup)
{
transaction->cleanup = cln->clean_next;
cln->TransactionRoutine(transaction->public_handle, cln->clean_arg);
free_block(cln);
}
}*/
#ifndef REQUESTER
static void event_ast(void* buffer_void,
USHORT length,
const UCHAR* items)
{
/**************************************
*
* e v e n t _ a s t
*
**************************************
*
* Functional description
* We're had an event complete.
*
**************************************/
memcpy(buffer_void, items, length);
ISC_event_post(why_event);
}
#endif
#ifndef REQUESTER
static void exit_handler(event_t* why_eventL)
{
/**************************************
*
* e x i t _ h a n d l e r
*
**************************************
*
* Functional description
* Cleanup shared image.
*
**************************************/
#ifdef WIN_NT
CloseHandle((void *) why_eventL->event_handle);
#endif
why_initialized = FALSE;
why_enabled = 0;
#if !(defined REQUESTER || defined SUPERCLIENT || defined SUPERSERVER)
isc_enter_count = 0;
subsystem_usage = 0;
subsystem_FPE_reset = FPE_RESET_INIT_ONLY;
#endif
}
#endif
static Transaction* find_transaction(Attachment* dbb,
Transaction* transaction)
{
/**************************************
*
* f i n d _ t r a n s a c t i o n
*
**************************************
*
* Functional description
* Find the element of a possible multiple database transaction
* that corresponds to the current database.
*
**************************************/
for (; transaction; transaction = transaction->next)
if (transaction->parent == dbb)
return transaction;
return NULL;
}
static void free_block(void* block)
{
/**************************************
*
* f r e e _ b l o c k
*
**************************************
*
* Functional description
* Release some memory.
*
**************************************/
gds__free((SLONG *) block);
}
static int get_database_info(ISC_STATUS * status,
Transaction* transaction,
TEXT ** ptr)
{
/**************************************
*
* g e t _ d a t a b a s e _ i n f o
*
**************************************
*
* Functional description
* Get the full database pathname
* and put it in the transaction
* description record.
*
**************************************/
// Look at the changed code: we don't support here more than 254 bytes in the path
// so it's better to truncate or we'll have corrupt data in the trans desc record:
// the length in one byte would wrap and we would copy more bytes that expected.
// Our caller (prepare) assumed each call consumes at most 256 bytes (item, len, data)
// hence if we don't check here, we have a B.O.
TEXT* p = *ptr;
Attachment* database = transaction->parent;
*p++ = TDR_DATABASE_PATH;
const TEXT* q = database->db_path.c_str();
size_t len = strlen(q);
if (len > 254)
len = 254;
*p++ = (TEXT) len;
memcpy(p, q, len);
*ptr = p + len;
return FB_SUCCESS;
}
static const PTR get_entrypoint(int proc,
int implementation)
{
/**************************************
*
* g e t _ e n t r y p o i n t
*
**************************************
*
* Functional description
* Lookup entrypoint for procedure.
*
**************************************/
ENTRY *ent = entrypoints + implementation * PROC_count + proc;
const PTR entrypoint = ent->address;
if (entrypoint)
{
return entrypoint;
}
#ifndef SUPERCLIENT
const TEXT* image = images[implementation].path;
const TEXT* name = ent->name;
if (!name)
{
name = generic[proc];
}
if (image && name)
{
PTR entry = (PTR) Jrd::Module::lookup(image, name);
if (entry)
{
ent->address = entry;
return entry;
}
}
#endif
return &no_entrypoint;
}
static USHORT sqlda_buffer_size(USHORT min_buffer_size, XSQLDA * sqlda,
USHORT dialect)
{
/**************************************
*
* s q l d a _ b u f f e r _ s i z e
*
**************************************
*
* Functional description
* Calculate size of a buffer that is large enough
* to store the info items relating to an SQLDA.
*
**************************************/
USHORT n_variables;
USHORT sql_dialect;
/* If dialect / 10 == 0, then it has not been combined with the
parser version for a prepare statement. If it has been combined, then
the dialect needs to be pulled out to compare to DIALECT_xsqlda
*/
if ((sql_dialect = dialect / 10) == 0)
sql_dialect = dialect;
if (!sqlda)
n_variables = 0;
else if (sql_dialect >= DIALECT_xsqlda)
n_variables = sqlda->sqln;
else
n_variables = ((SQLDA *) sqlda)->sqln;
ULONG length = 32 + n_variables * 172;
if (length < min_buffer_size)
length = min_buffer_size;
return (USHORT)((length > 65500L) ? 65500L : length);
}
static ISC_STATUS get_transaction_info(ISC_STATUS* user_status,
Transaction* transaction,
TEXT** ptr)
{
/**************************************
*
* g e t _ t r a n s a c t i o n _ i n f o
*
**************************************
*
* Functional description
* Put a transaction's id into the transaction
* description record.
*
**************************************/
Status status(user_status);
try
{
TEXT buffer[16];
TEXT* p = *ptr;
status.ok();
if (CALL(PROC_TRANSACTION_INFO, transaction->implementation) (status,
&transaction->
handle,
sizeof
(prepare_tr_info),
prepare_tr_info,
sizeof
(buffer),
buffer))
{
return status[1];
}
const TEXT* q = buffer + 3;
*p++ = TDR_TRANSACTION_ID;
USHORT length = (USHORT)gds__vax_integer(reinterpret_cast<UCHAR*>(buffer + 1), 2);
// Prevent information out of sync.
if (length > MAX_UCHAR)
length = MAX_UCHAR;
*p++ = length;
memcpy(p, q, length);
*ptr = p + length;
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
static void iterative_sql_info(ISC_STATUS * user_status,
FB_API_HANDLE* stmt_handle,
SSHORT item_length,
const SCHAR * items,
SSHORT buffer_length,
SCHAR* buffer,
USHORT dialect,
XSQLDA * sqlda)
{
/**************************************
*
* i t e r a t i v e _ s q l _ i n f o
*
**************************************
*
* Functional description
* Turn an sql info buffer into an SQLDA. If the info
* buffer was incomplete, make another request, beginning
* where the previous info call left off.
*
**************************************/
USHORT last_index;
SCHAR new_items[32];
while (UTLD_parse_sql_info( user_status,
dialect,
buffer,
sqlda,
&last_index) && last_index)
{
char* p = new_items;
*p++ = isc_info_sql_sqlda_start;
*p++ = 2;
*p++ = last_index;
*p++ = last_index >> 8;
memcpy(p, items, (int) item_length);
p += item_length;
if (GDS_DSQL_SQL_INFO( user_status,
stmt_handle,
(SSHORT) (p - new_items),
new_items,
buffer_length,
buffer))
{
break;
}
}
}
static ISC_STATUS open_blob(ISC_STATUS* user_status,
FB_API_HANDLE* db_handle,
FB_API_HANDLE* tra_handle,
FB_API_HANDLE* public_blob_handle,
SLONG* blob_id,
USHORT bpb_length,
const UCHAR* bpb,
SSHORT proc,
SSHORT proc2)
{
/**************************************
*
* o p e n _ b l o b
*
**************************************
*
* Functional description
* Open an existing blob (extended edition).
*
**************************************/
YEntry status(user_status);
StBlb* blob_handle = 0;
try
{
nullCheck(public_blob_handle, isc_bad_segstr_handle);
Attachment* dbb = translate<Attachment>(db_handle);
status.setPrimaryHandle(dbb);
Transaction* transaction = findTransaction(tra_handle, dbb);
USHORT flags = 0;
USHORT from, to;
gds__parse_bpb(bpb_length, bpb, &from, &to);
if (get_entrypoint(proc2, dbb->implementation) != no_entrypoint &&
CALL(proc2, dbb->implementation) (status,
&dbb->handle,
&transaction->handle,
&blob_handle,
blob_id,
bpb_length,
bpb) != isc_unavailable)
{
flags = 0;
}
else if (!to || from == to)
{
CALL(proc, dbb->implementation) (status,
&dbb->handle,
&transaction->handle,
&blob_handle, blob_id);
}
if (status[1]) {
return status[1];
}
Blob* blob = new Blob(blob_handle, public_blob_handle, dbb);
blob->flags |= flags;
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(status);
}
return status[1];
}
extern "C" {
static ISC_STATUS no_entrypoint(ISC_STATUS * user_status, ...)
{
/**************************************
*
* n o _ e n t r y p o i n t
*
**************************************
*
* Functional description
* No_entrypoint is called if there is not entrypoint for a given routine.
*
**************************************/
*user_status++ = isc_arg_gds;
*user_status++ = isc_unavailable;
*user_status = isc_arg_end;
return isc_unavailable;
}
} // extern "C"
static ISC_STATUS prepare(ISC_STATUS* user_status,
Transaction* transaction)
{
/**************************************
*
* p r e p a r e
*
**************************************
*
* Functional description
* Perform the first phase of a two-phase commit
* for a multi-database transaction.
*
**************************************/
Status status(user_status);
status.ok();
Transaction* sub;
TEXT tdr_buffer[1024];
size_t length = 0;
int transcount = 0;
for (sub = transaction->next; sub; sub = sub->next)
{
length += 256;
++transcount;
}
// To do: use transcount to check the maximum allowed dbs in a two phase commit.
TEXT host[64];
ISC_get_host(host, sizeof(host));
const size_t hostlen = strlen(host);
length += hostlen + 3; // TDR_version + TDR_host_site + UCHAR(strlen(host))
TEXT* const description = (length > sizeof(tdr_buffer)) ?
(TEXT *) gds__alloc(length) : tdr_buffer;
/* build a transaction description record containing
the host site and database/transaction
information for the target databases. */
TEXT* p = description;
if (!p)
{
status[0] = isc_arg_gds;
status[1] = isc_virmemexh;
status[2] = isc_arg_end;
return status[1];
}
*p++ = TDR_VERSION;
*p++ = TDR_HOST_SITE;
*p++ = UCHAR(hostlen);
memcpy(p, host, hostlen);
p += hostlen;
/* Get database and transaction stuff for each sub-transaction */
for (sub = transaction->next; sub; sub = sub->next) {
get_database_info(status, sub, &p);
get_transaction_info(status, sub, &p);
}
/* So far so good -- prepare each sub-transaction */
length = p - description;
for (sub = transaction->next; sub; sub = sub->next)
if (CALL(PROC_PREPARE, sub->implementation) (status,
&sub->handle,
length, description))
{
if (description != tdr_buffer) {
free_block(description);
}
return status[1];
}
if (description != tdr_buffer)
free_block(description);
return FB_SUCCESS;
}
inline void why_priv_gds__free_if_set(SCHAR** pMem)
{
if (*pMem)
{
gds__free(*pMem);
*pMem = 0;
}
}
static void release_dsql_support(sqlda_sup& dasup)
{
/**************************************
*
* r e l e a s e _ d s q l _ s u p p o r t
*
**************************************
*
* Functional description
* Release some memory.
*
**************************************/
sqlda_sup::dasup_clause* pClauses = dasup.dasup_clauses;
why_priv_gds__free_if_set(&pClauses[DASUP_CLAUSE_bind].dasup_blr);
why_priv_gds__free_if_set(&pClauses[DASUP_CLAUSE_select].dasup_blr);
why_priv_gds__free_if_set(&pClauses[DASUP_CLAUSE_bind].dasup_msg);
why_priv_gds__free_if_set(&pClauses[DASUP_CLAUSE_select].dasup_msg);
why_priv_gds__free_if_set(&pClauses[DASUP_CLAUSE_bind].dasup_info_buf);
why_priv_gds__free_if_set(&pClauses[DASUP_CLAUSE_select].dasup_info_buf);
}
static void save_error_string(ISC_STATUS * status)
{
/**************************************
*
* s a v e _ e r r o r _ s t r i n g
*
**************************************
*
* Functional description
* This is need because there are cases
* where the memory allocated for strings
* in the status vector is freed prior to
* surfacing them to the user. This is an
* attempt to save off 1 string to surface to
* the user. Any other strings will be set to
* a standard <Unknown> string.
*
**************************************/
fb_assert(status != NULL);
TEXT* p = glbstr1;
ULONG len = sizeof(glbstr1) - 1;
while (*status != isc_arg_end)
{
ULONG l;
switch (*status++)
{
case isc_arg_cstring:
l = (ULONG) * status;
if (l < len)
{
status++; /* Length is unchanged */
/*
* This strncpy should really be a memcpy
*/
strncpy(p, reinterpret_cast<char*>(*status), l);
*status++ = (ISC_STATUS) p; /* string in static memory */
p += l;
len -= l;
}
else {
*status++ = (ISC_STATUS) strlen(glbunknown);
*status++ = (ISC_STATUS) glbunknown;
}
break;
case isc_arg_interpreted:
case isc_arg_string:
l = (ULONG) strlen(reinterpret_cast<char*>(*status)) + 1;
if (l < len)
{
strncpy(p, reinterpret_cast<char*>(*status), l);
*status++ = (ISC_STATUS) p; /* string in static memory */
p += l;
len -= l;
}
else
{
*status++ = (ISC_STATUS) glbunknown;
}
break;
default:
fb_assert(FALSE);
case isc_arg_gds:
case isc_arg_number:
case isc_arg_vms:
case isc_arg_unix:
case isc_arg_win32:
status++; /* Skip parameter */
break;
}
}
}
static bool set_path(const Firebird::PathName& file_name, Firebird::PathName& expanded_name)
{
/**************************************
*
* s e t _ p a t h
*
**************************************
*
* Functional description
* Set a prefix to a filename based on
* the ISC_PATH user variable.
*
**************************************/
// look for the environment variables to tack
// onto the beginning of the database path
Firebird::PathName pathname;
if (!fb_utils::readenv("ISC_PATH", pathname))
return false;
// if the file already contains a remote node
// or any path at all forget it
for (const TEXT* p = file_name.c_str(); *p; p++)
{
if (*p == ':' || *p == '/' || *p == '\\')
return false;
}
// concatenate the strings
expanded_name = pathname;
// CVC: Make the concatenation work if no slash is present.
char lastChar = expanded_name[expanded_name.length() - 1];
if (lastChar != ':' && lastChar != '/' && lastChar != '\\') {
expanded_name.append(PathUtils::dir_sep);
}
expanded_name.append(file_name);
return true;
}
static void subsystem_enter(void) throw()
{
/**************************************
*
* s u b s y s t e m _ e n t e r
*
**************************************
*
* Functional description
* Enter subsystem.
*
**************************************/
try
{
THREAD_ENTER();
#if !(defined REQUESTER || defined SUPERCLIENT || defined SUPERSERVER)
isc_enter_count++;
if (subsystem_usage == 0 ||
(subsystem_FPE_reset &
(FPE_RESET_NEXT_API_CALL | FPE_RESET_ALL_API_CALL)))
{
ISC_enter();
subsystem_FPE_reset &= ~FPE_RESET_NEXT_API_CALL;
}
#endif
#ifdef DEBUG_FPE_HANDLING
{
/* It's difficult to make a FPE to occur inside the engine - for debugging
* just force one to occur every-so-often. */
static ULONG counter = 0;
if (((counter++) % 10) == 0)
{
fprintf(stderr, "Forcing FPE to occur within engine\n");
kill(getpid(), SIGFPE);
}
}
#endif /* DEBUG_FPE_HANDLING */
}
catch(const Firebird::Exception&)
{
// ToDo: show full exception message here
gds__log("Unexpected exception in subsystem_enter()");
}
}
static void subsystem_exit(void) throw()
{
/**************************************
*
* s u b s y s t e m _ e x i t
*
**************************************
*
* Functional description
* Exit subsystem.
*
**************************************/
try
{
#if !(defined REQUESTER || defined SUPERCLIENT || defined SUPERSERVER)
if (subsystem_usage == 0 ||
(subsystem_FPE_reset &
(FPE_RESET_NEXT_API_CALL | FPE_RESET_ALL_API_CALL)))
{
ISC_exit();
}
isc_enter_count--;
#endif
THREAD_EXIT();
}
catch(const Firebird::Exception&)
{
// ToDo: show full exception message here
gds__log("Unexpected exception in subsystem_exit()");
}
}
#if !defined (SUPERCLIENT) && !defined (REQUESTER)
BOOLEAN WHY_set_shutdown(BOOLEAN flag)
{
/**************************************
*
* W H Y _ s e t _ s h u t d o w n
*
**************************************
*
* Functional description
* Set shutdown_flag to either TRUE or FALSE.
* TRUE = accept new connections
* FALSE= refuse new connections
* Returns the prior state of the flag (server).
*
**************************************/
const BOOLEAN old_flag = shutdown_flag;
shutdown_flag = flag;
return old_flag;
}
BOOLEAN WHY_get_shutdown()
{
/**************************************
*
* W H Y _ g e t _ s h u t d o w n
*
**************************************
*
* Functional description
* Returns the current value of shutdown_flag.
*
**************************************/
return shutdown_flag;
}
#endif /* SERVER_SHUTDOWN && !SUPERCLIENT && !REQUESTER */