8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-25 00:03:03 +01:00
firebird-mirror/src/remote/server.cpp

4994 lines
121 KiB
C++

/*
* PROGRAM: JRD Remote Server
* MODULE: server.c
* DESCRIPTION: Remote server
*
* 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): ______________________________________.
*/
#include "firebird.h"
#include "../jrd/ib_stdio.h"
#include <string.h>
#include "../jrd/gds.h"
#include "../jrd/gdsassert.h"
#include "../remote/remote.h"
#include "../jrd/thd.h"
#include "../jrd/isc.h"
#include "../jrd/license.h"
#include "../jrd/time.h"
#include "../remote/merge_proto.h"
#include "../remote/parse_proto.h"
#include "../remote/remot_proto.h"
#include "../remote/serve_proto.h"
#ifdef WIN_NT
#include "../remote/cntl_proto.h"
#include <stdlib.h>
#endif
#include "../jrd/gds_proto.h"
#include "../jrd/isc_proto.h"
#include "../jrd/isc_s_proto.h"
#include "../jrd/sch_proto.h"
#include "../jrd/thd_proto.h"
#include "../jrd/why_proto.h"
#ifdef SUPERSERVER
#include "../jrd/isc_i_proto.h"
#endif
#include "../remote/proto_proto.h" // xdr_protocol_overhead()
#define SET_THREAD_DATA trdb = &thd_context;\
trdb->trdb_status_vector = NULL;\
THD_put_specific ((THDD) trdb);\
trdb->trdb_thd_data.thdd_type = THDD_TYPE_TRDB
#define RESTORE_THREAD_DATA THD_restore_specific()
/** CHECK_HANDLE checks -
if the id passwd is within the vector bounds,
that the port_object corresponding to the id is not null,
if the port_object's type matches with the expected type
**/
#pragma FB_COMPILER_MESSAGE("What kind of crap is this?! FIX!")
#define CHECK_HANDLE(blk,cast,type,id,err) \
{ \
if (id >= port->port_object_vector->vec_count || \
!(blk = (cast) port->port_objects [id]) || \
((BLK) blk)->blk_type != (UCHAR) type) \
{ \
status_vector [0] = isc_arg_gds; \
status_vector [1] = err; \
status_vector [2] = isc_arg_end; \
return port->send_response(send, 0, 0, status_vector); \
} \
}
#define CHECK_HANDLE_MEMBER(blk,cast,type,id,err) \
{ \
if (id >= this->port_object_vector->vec_count || \
!(blk = (cast) this->port_objects [id]) || \
((BLK) blk)->blk_type != (UCHAR) type) \
{ \
status_vector [0] = isc_arg_gds; \
status_vector [1] = err; \
status_vector [2] = isc_arg_end; \
return this->send_response(send, 0, 0, status_vector); \
} \
}
#define STMT_BLOB 1
#define STMT_NO_BATCH 2
#define STMT_OTHER 0
typedef struct server_req_t
{
server_req_t* req_next;
server_req_t* req_chain;
PORT req_port;
PACKET req_send;
PACKET req_receive;
} *SERVER_REQ;
typedef struct srvr
{
struct srvr* srvr_next;
struct port* srvr_parent_port;
enum rem_port_t srvr_port_type;
USHORT srvr_flags;
} *SRVR;
static BOOLEAN accept_connection(PORT, P_CNCT*, PACKET*);
static STATUS allocate_statement(PORT, P_RLSE*, PACKET*);
static SLONG append_request_chain(SERVER_REQ, SERVER_REQ*);
static SLONG append_request_next(SERVER_REQ, SERVER_REQ*);
static STATUS attach_database(PORT, P_OP, P_ATCH*, PACKET*);
static void aux_connect(PORT, P_REQ*, PACKET*);
static void aux_request(PORT, P_REQ*, PACKET*);
static STATUS cancel_events(PORT, P_EVENT*, PACKET*);
#ifdef CANCEL_OPERATION
static void cancel_operation(PORT);
#endif
static BOOLEAN check_request(RRQ, USHORT, USHORT);
static USHORT check_statement_type(RSR);
#ifdef SCROLLABLE_CURSORS
static MSG dump_cache(rrq::rrq_repeat*);
#endif
static USHORT get_next_msg_no(RRQ, USHORT, USHORT*);
static RTR make_transaction(RDB, int*);
static void release_blob(RBL);
static void release_event(RVNT);
static void release_request(RRQ);
static void release_statement(RSR*);
static void release_sql_request(RSR);
static void release_transaction(RTR);
#ifdef SCROLLABLE_CURSORS
static MSG scroll_cache(rrq::rrq_repeat*, USHORT *, ULONG *);
#endif
static void server_ast(RVNT, USHORT, UCHAR *);
static void success(STATUS *);
static int THREAD_ROUTINE thread(void *);
static void zap_packet(PACKET*, BOOLEAN);
// static data - NOT THREAD SAFE!
static SLONG threads_waiting = 0;
static SLONG extra_threads = 0;
static SERVER_REQ request_que = NULL;
static SERVER_REQ free_requests = NULL;
static SERVER_REQ active_requests = NULL;
static EVENT_T thread_event[1];
static SRVR servers;
static CONST UCHAR request_info[] =
{
gds_info_state,
gds_info_message_number,
gds_info_end
};
static CONST UCHAR sql_info[] =
{
gds_info_sql_stmt_type,
isc_info_sql_batch_fetch
};
#define GDS_DSQL_ALLOCATE isc_dsql_allocate_statement
#define GDS_DSQL_EXECUTE isc_dsql_execute2_m
#define GDS_DSQL_EXECUTE_IMMED isc_dsql_exec_immed3_m
#define GDS_DSQL_FETCH isc_dsql_fetch_m
#define GDS_DSQL_FREE isc_dsql_free_statement
#define GDS_DSQL_INSERT isc_dsql_insert_m
#define GDS_DSQL_PREPARE isc_dsql_prepare_m
#define GDS_DSQL_SET_CURSOR isc_dsql_set_cursor_name
#define GDS_DSQL_SQL_INFO isc_dsql_sql_info
void SRVR_main( PORT main_port, USHORT flags)
{
/**************************************
*
* S R V R _ m a i n
*
**************************************
*
* Functional description
* Main entrypoint of server.
*
**************************************/
PACKET send, receive;
PORT port;
#ifdef SUPERSERVER
ISC_enter(); /* Setup floating point exception handler once and for all. */
#endif
zap_packet(&receive, TRUE);
zap_packet(&send, TRUE);
THREAD_ENTER;
set_server(main_port, flags);
while (TRUE)
{
//
// Note: The following is cloned in server other SRVR_main instances.
//
port = main_port->receive(&receive);
if (!port) {
break;
}
if (!process_packet(port, &send, &receive, 0))
break;
}
THREAD_EXIT;
}
void SRVR_multi_thread( PORT main_port, USHORT flags)
{
/**************************************
*
* S R V R _ m u l t i _ t h r e a d
*
**************************************
*
* Functional description
* Multi-threaded flavor of server.
*
**************************************/
#if (defined PC_PLATFORM && !defined NETWARE_386)
/* for PC router, no multithreading is available */
return;
#else
SERVER_REQ request = NULL, active;
VOLATILE PORT port = NULL;
SLONG pending_requests;
P_OP operation;
#ifdef DEV_BUILD
#ifdef DEBUG
SSHORT request_count = 0;
#endif /* DEBUG */
#endif /* DEV_BUILD */
struct trdb thd_context, *trdb;
STATUS status_vector[20];
gds__thread_enable(-1);
ISC_event_init(thread_event, 0, 0);
THREAD_ENTER;
SET_THREAD_DATA;
trdb->trdb_status_vector = status_vector;
try {
set_server(main_port, flags);
/* We need to have this error handler as there is so much underlaying code
* that is depending on it being there. The expected failure
* that can occur in the call paths from here is out-of-memory
* and it is unknown if other failures can get us here
* Failures can occur from set_server as well as RECEIVE
*
* Note that if a failure DOES occur, we reenter the loop and try to continue
* operations. This is important as this is the main receive loop for
* new incoming requests, if we exit here no new requests can come to the
* server.
*/
try {
/* When this loop exits, the server will no longer receive requests */
while (TRUE)
{
port = NULL;
/* Allocate a memory block to store the request in */
if (request = free_requests)
{
free_requests = request->req_next;
}
else
{
/* No block on the free list - allocate some new memory */
request = (SERVER_REQ) gds__alloc((SLONG) sizeof(struct server_req_t));
if (request)
{
zap_packet(&request->req_send, TRUE);
zap_packet(&request->req_receive, TRUE);
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("SRVR_multi_thread allocate request %x\n",
request);
#endif
}
else
{
/* System is out of memory, let's delay processing this
request and hope another thread will free memory or
request blocks that we can then use. */
THREAD_EXIT;
THREAD_SLEEP(1 * 1000);
THREAD_ENTER;
continue;
}
}
#ifdef DEV_BUILD
#ifdef DEBUG
if ((request_count++ % 4) == 0) {
Firebird::status_exception::raise(gds__virmemexh);
}
#endif /* DEBUG */
#endif /* DEV_BUILD */
if (request) {
request->req_next = NULL;
request->req_chain = NULL;
/* We have a request block - now get some information to stick into it */
if (!(port = main_port->receive(&request->req_receive)))
{
gds__log("SRVR_multi_thread/RECEIVE: error on main_port, shutting down");
THREAD_EXIT;
RESTORE_THREAD_DATA;
return;
}
request->req_port = port;
operation = request->req_receive.p_operation;
#ifdef DEV_BUILD
#ifdef DEBUG
if ((request_count % 5) == 0) {
Firebird::status_exception::raise(gds__virmemexh);
}
#endif /* DEBUG */
#endif /* DEV_BUILD */
/* If port has an active request, link this one in */
for (active = active_requests; active; active = active->req_next)
if (active->req_port == port) {
/* Don't queue a dummy keepalive packet if there is
an active request running on this port. */
if (operation == op_dummy) {
request->req_next = free_requests;
free_requests = request;
goto finished;
}
port->port_requests_queued++;
append_request_chain(request, &active->req_chain);
#ifdef REMOTE_DEBUG_MEMORY
ib_printf
("SRVR_multi_thread ACTIVE request_queued %d\n",
port->port_requests_queued);
ib_fflush(ib_stdout);
#endif
#ifdef CANCEL_OPERATION
if (operation == op_exit || operation == op_disconnect)
cancel_operation(port);
#endif
goto finished;
}
/* If port has an pending request, link this one in */
for (active = request_que; active; active = active->req_next)
{
if (active->req_port == port)
{
/* Don't queue a dummy keepalive packet if there is
a pending request against this port. */
if (operation == op_dummy) {
request->req_next = free_requests;
free_requests = request;
goto finished;
}
port->port_requests_queued++;
append_request_chain(request, &active->req_chain);
#ifdef REMOTE_DEBUG_MEMORY
ib_printf
("SRVR_multi_thread PENDING request_queued %d\n",
port->port_requests_queued);
ib_fflush(ib_stdout);
#endif
#ifdef CANCEL_OPERATION
if (operation == op_exit || operation == op_disconnect)
cancel_operation(port);
#endif
goto finished;
}
}
/* No port to assign request to, add it to the waiting queue and wake up a
* thread to handle it
*/
pending_requests = append_request_next(request, &request_que);
port->port_requests_queued++;
#ifdef REMOTE_DEBUG_MEMORY
ib_printf
("SRVR_multi_thread APPEND_PENDING request_queued %d\n",
port->port_requests_queued);
ib_fflush(ib_stdout);
#endif
/* NOTE: we really *should* have something that limits how many
* total threads we allow in the system. As each thread will
* eat up memory that other threads could use to complete their work
*/
/* NOTE: The setting up of extra_threads variable is done below to let waiting
threads know if their services may be needed for the current set
of requests. Otherwise, some idle threads may leave the system
freeing up valuable memory.
*/
extra_threads = threads_waiting - pending_requests;
if (extra_threads < 0) {
gds__thread_start( reinterpret_cast<FPTR_INT_VOID_PTR>(thread),
(void*)flags,
THREAD_medium,
THREAD_ast,
0);
}
ISC_event_post(thread_event);
}
finished:
;
}
/* We should never get to this point */
THREAD_EXIT;
} // try
catch (...)
{
/* If we got as far as having a port allocated before the error, disconnect it
* gracefully.
*/
if (port != NULL)
{
#ifdef DEV_BUILD
#ifdef DEBUG
ConsolePrintf("%%ISERVER-F-NOPORT, no port in a storm\r\n");
#endif /* DEBUG */
#endif /* DEV_BUILD */
gds__log("SRVR_multi_thread: forcefully disconnecting a port");
/* To handle recursion within the error handler */
try {
/* If we have a port, request really should be non-null, but just in case ... */
if (request != NULL) {
/* Send client a real status indication of why we disconnected them */
/* Note that send_response() can post errors that wind up in this same handler */
#ifdef DEV_BUILD
#ifdef DEBUG
ConsolePrintf
("%%ISERVER-F-NOMEM, virtual memory exhausted\r\n");
#endif /* DEBUG */
#endif /* DEV_BUILD */
port->send_response(&request->req_send, 0, 0,
status_vector);
port->disconnect(&request->req_send, &request->req_receive);
}
else {
/* Can't tell the client much, just make 'em go away. Their side should detect
* a network error
*/
port->disconnect(NULL, NULL);
}
port = NULL;
} // try
catch (...) {
port->disconnect(NULL, NULL);
port = NULL;
}
}
/* There was an error in the processing of the request, if we have allocated
* a request, free it up and continue.
*/
if (request != NULL) {
request->req_next = free_requests;
free_requests = request;
request = NULL;
}
}
} // try
catch (...) {
/* Some kind of unhandled error occured during server setup. In lieu
* of anything we CAN do, log something (and we might be so hosed
* we can't log anything) and give up.
* The likely error here is out-of-memory.
*/
gds__log("SRVR_multi_thread: error during startup, shutting down");
RESTORE_THREAD_DATA;
THREAD_EXIT;
return;
}
#endif
/* Why isn't this inside the #endif above? */
RESTORE_THREAD_DATA;
}
static BOOLEAN accept_connection( PORT port, P_CNCT * connect, PACKET* send)
{
/**************************************
*
* a c c e p t _ c o n n e c t i o n
*
**************************************
*
* Functional description
* Process a connect packet.
*
**************************************/
P_ACPT *accept;
P_ARCH architecture;
USHORT weight, version, type;
p_cnct::p_cnct_repeat * protocol, *end;
TEXT buffer[64];
/* Accept the physical connection */
send->p_operation = op_reject;
accept = &send->p_acpt;
weight = 0;
if (!port->accept(connect)) {
port->send(send);
return FALSE;
}
/* Select the most appropriate protocol (this will get smarter) */
protocol = connect->p_cnct_versions;
for (end = protocol + connect->p_cnct_count; protocol < end; protocol++)
if ((protocol->p_cnct_version == PROTOCOL_VERSION3 ||
protocol->p_cnct_version == PROTOCOL_VERSION4 ||
protocol->p_cnct_version == PROTOCOL_VERSION5 ||
protocol->p_cnct_version == PROTOCOL_VERSION6 ||
protocol->p_cnct_version == PROTOCOL_VERSION7 ||
protocol->p_cnct_version == PROTOCOL_VERSION8 ||
protocol->p_cnct_version == PROTOCOL_VERSION9 ||
protocol->p_cnct_version == PROTOCOL_VERSION10
#ifdef SCROLLABLE_CURSORS
|| protocol->p_cnct_version == PROTOCOL_SCROLLABLE_CURSORS
#endif
) &&
(protocol->p_cnct_architecture == arch_generic ||
protocol->p_cnct_architecture == ARCHITECTURE) &&
protocol->p_cnct_weight >= weight) {
weight = protocol->p_cnct_weight;
version = protocol->p_cnct_version;
architecture = protocol->p_cnct_architecture;
type = MIN(protocol->p_cnct_max_type, ptype_out_of_band);
send->p_operation = op_accept;
}
/* Send off out gracious acceptance or flag rejection */
accept->p_acpt_version = port->port_protocol = version;
accept->p_acpt_architecture = architecture;
accept->p_acpt_type = type;
/* and modify the version string to reflect the chosen protocol */
sprintf(buffer, "%s/P%d", port->port_version->str_data,
port->port_protocol);
ALLR_free(port->port_version);
port->port_version = REMOTE_make_string(buffer);
if (architecture == ARCHITECTURE)
port->port_flags |= PORT_symmetric;
if (type == ptype_rpc)
port->port_flags |= PORT_rpc;
if (type != ptype_out_of_band)
port->port_flags |= PORT_no_oob;
port->send(send);
return TRUE;
}
static STATUS allocate_statement( PORT port, P_RLSE * allocate, PACKET* send)
{
/**************************************
*
* a l l o c a t e _ s t a t e m e n t
*
**************************************
*
* Functional description
* Allocate a statement handle.
*
**************************************/
RDB rdb;
RSR statement;
int *handle;
OBJCT object;
STATUS status_vector[20];
rdb = port->port_context;
handle = NULL;
THREAD_EXIT;
GDS_DSQL_ALLOCATE(status_vector,
reinterpret_cast<void**>(&rdb->rdb_handle),
reinterpret_cast<void**>(&handle));
THREAD_ENTER;
if (status_vector[1])
object = 0;
else {
/* Allocate SQL request block */
statement = (RSR) ALLOC(type_rsr);
statement->rsr_rdb = rdb;
statement->rsr_handle = handle;
if (statement->rsr_id = port->get_id(&statement->rsr_header))
{
object = statement->rsr_id;
statement->rsr_next = rdb->rdb_sql_requests;
rdb->rdb_sql_requests = statement;
}
else {
object = 0;
THREAD_EXIT;
GDS_DSQL_FREE(status_vector,
reinterpret_cast<void**>(GDS_REF(statement->rsr_handle)),
DSQL_drop);
THREAD_ENTER;
ALLR_release(statement);
status_vector[0] = isc_arg_gds;
status_vector[1] = isc_too_many_handles;
status_vector[2] = isc_arg_end;
}
}
return port->send_response(send, object, 0, status_vector);
}
static SLONG append_request_chain( SERVER_REQ request, SERVER_REQ * que)
{
/**************************************
*
* a p p e n d _ r e q u e s t _ c h a i n
*
**************************************
*
* Functional description
* Traverse using req_chain ptr and append
* a request at the end of a que.
* Return count of pending requests.
*
**************************************/
SLONG requests;
for (requests = 1; *que; que = &(*que)->req_chain)
++requests;
*que = request;
return requests;
}
static SLONG append_request_next( SERVER_REQ request, SERVER_REQ * que)
{
/**************************************
*
* a p p e n d _ r e q u e s t _ n e x t
*
**************************************
*
* Functional description
* Traverse using req_next ptr and append
* a request at the end of a que.
* Return count of pending requests.
*
**************************************/
SLONG requests;
for (requests = 1; *que; que = &(*que)->req_next)
++requests;
*que = request;
return requests;
}
static STATUS attach_database(
PORT port,
P_OP operation, P_ATCH * attach, PACKET* send)
{
/**************************************
*
* a t t a c h _ d a t a b a s e
*
**************************************
*
* Functional description
* Process an attach or create packet.
*
**************************************/
USHORT l, dl;
UCHAR *file, *dpb, new_dpb_buffer[512], *new_dpb, *p, *end;
int *handle;
STATUS status_vector[20];
RDB rdb;
STR string;
send->p_operation = op_accept;
handle = NULL;
file = attach->p_atch_file.cstr_address;
l = attach->p_atch_file.cstr_length;
dpb = attach->p_atch_dpb.cstr_address;
dl = attach->p_atch_dpb.cstr_length;
/* If we have user identification, append it to database parameter block */
new_dpb = new_dpb_buffer;
if (string = port->port_user_name) {
if ((dl + 3 + string->str_length) > sizeof(new_dpb_buffer))
new_dpb = ALLR_alloc((SLONG) (dl + 3 + string->str_length));
p = new_dpb;
if (dl)
for (end = dpb + dl; dpb < end;)
*p++ = *dpb++;
else
*p++ = gds_dpb_version1;
*p++ = gds_dpb_sys_user_name;
*p++ = (UCHAR) string->str_length;
for (dpb = (UCHAR *) string->str_data, end = dpb + string->str_length;
dpb < end;)
*p++ = *dpb++;
dpb = new_dpb;
dl = p - new_dpb;
}
/* See if user has specified parameters relevent to the connection,
they will be stuffed in the DPB if so. */
REMOTE_get_timeout_params(port, dpb, dl);
THREAD_EXIT;
if (operation == op_attach)
{
isc_attach_database(status_vector,
l,
reinterpret_cast<char*>(GDS_VAL(file)),
reinterpret_cast<void**>(GDS_REF(handle)),
dl,
reinterpret_cast<char*>(GDS_VAL(dpb)));
}
else
{
isc_create_database(status_vector,
l,
reinterpret_cast<char*>(GDS_VAL(file)),
reinterpret_cast<void**>(GDS_REF(handle)),
dl,
reinterpret_cast<char*>(GDS_VAL(dpb)),
0);
}
THREAD_ENTER;
if (new_dpb != new_dpb_buffer) {
ALLR_free(new_dpb);
}
if (!status_vector[1])
{
port->port_context = rdb = (RDB) ALLOC(type_rdb);
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("attach_databases(server) allocate rdb %x\n", rdb);
#endif
rdb->rdb_port = port;
rdb->rdb_handle = handle;
}
return port->send_response(send, 0, 0, status_vector);
}
static void aux_connect( PORT port, P_REQ * request, PACKET* send)
{
/**************************************
*
* a u x _ c o n n e c t
*
**************************************
*
* Functional description
* We're receive a auxiliary connection on the main communications
* channel. Accept connection and reply politely.
*
**************************************/
PORT partner;
port->connect(0, 0);
partner = (PORT) request->p_req_partner;
partner->port_async = port;
}
static void aux_request( PORT port, P_REQ * request, PACKET* send)
{
/**************************************
*
* a u x _ r e q u e s t
*
**************************************
*
* Functional description
* Other guy wants to establish a secondary connection.
* Humor him.
*
**************************************/
STATUS *save_status, status_vector[20];
PORT aux_port;
RDB rdb;
UCHAR buffer[12];
CSTRING save_cstring;
/* save the port status vector */
save_status = port->port_status_vector;
port->port_status_vector = status_vector;
success(status_vector);
/* We do this silliness with buffer because the SPX protocol
requires a 12 byte buffer to be sent back. Other protocols
can do what they want to with cstr_address. */
save_cstring = send->p_resp.p_resp_data;
send->p_resp.p_resp_data.cstr_address = buffer;
aux_port = port->request(send);
rdb = port->port_context;
port->send_response(send, rdb->rdb_id,
send->p_resp.p_resp_data.cstr_length, status_vector);
if (status_vector[1]) {
/* restore the port status vector */
port->port_status_vector = save_status;
send->p_resp.p_resp_data = save_cstring;
return;
}
if (aux_port) {
aux_port->connect(send, 0);
aux_port->port_context = rdb;
}
/* restore the port status vector */
port->port_status_vector = save_status;
send->p_resp.p_resp_data = save_cstring;
}
static STATUS cancel_events( PORT port, P_EVENT * stuff, PACKET* send)
{
/**************************************
*
* c a n c e l _ e v e n t s
*
**************************************
*
* Functional description
* Cancel events.
*
**************************************/
RDB rdb;
RVNT event;
STATUS status_vector[20];
/* Which database ? */
rdb = port->port_context;
/* Find the event */
for (event = rdb->rdb_events; event; event = event->rvnt_next)
if (event->rvnt_rid == stuff->p_event_rid)
break;
/* If no event found, pretend it was cancelled */
if (!event)
return port->send_response(send, 0, 0, status_vector);
/* cancel the event */
if (event->rvnt_id) {
THREAD_EXIT;
isc_cancel_events(status_vector,
reinterpret_cast<void**>(GDS_REF(rdb->rdb_handle)),
GDS_REF(event->rvnt_id));
THREAD_ENTER;
}
/* zero event info */
event->rvnt_id = 0L;
event->rvnt_rid = 0L;
event->rvnt_ast = 0L;
/* return response */
return port->send_response(send, 0, 0, status_vector);
}
#ifdef CANCEL_OPERATION
static void cancel_operation( PORT port)
{
/**************************************
*
* c a n c e l _ o p e r a t i o n
*
**************************************
*
* Functional description
* Flag a running operation for cancel.
* Service operations are not currently
* able to be canceled.
*
**************************************/
RDB rdb;
STATUS status_vector[20];
if ((port->port_flags & (PORT_async | PORT_disconnect)) ||
!(rdb = port->port_context)) return;
if (rdb->rdb_handle)
if (!(rdb->rdb_flags & RDB_service)) {
THREAD_EXIT;
gds__cancel_operation(status_vector,
(struct hndl **) GDS_REF(rdb->rdb_handle),
CANCEL_raise);
THREAD_ENTER;
}
}
#endif
static BOOLEAN check_request(
RRQ request,
USHORT incarnation, USHORT msg_number)
{
/**************************************
*
* c h e c k _ r e q u e s t
*
**************************************
*
* Functional description
* Check to see if a request is ready to send us a particular
* message. If so, return TRUE, otherwise FALSE.
*
**************************************/
USHORT n;
if (!get_next_msg_no(request, incarnation, &n))
return FALSE;
return (msg_number == n) ? TRUE : FALSE;
}
static USHORT check_statement_type( RSR statement)
{
/**************************************
*
* c h e c k _ s t a t e m e n t _ t y p e
*
**************************************
*
* Functional description
* Return the type of SQL statement.
*
**************************************/
UCHAR buffer[16], *info;
USHORT l, type;
STATUS local_status[20];
USHORT ret;
BOOLEAN done = FALSE;
ret = STMT_OTHER;
THREAD_EXIT;
if (!GDS_DSQL_SQL_INFO(local_status,
reinterpret_cast<void**>(&statement->rsr_handle),
sizeof(sql_info),
(SCHAR *) sql_info, /* const_cast */
sizeof(buffer),
reinterpret_cast<char*>(buffer)))
{
for (info = buffer; (*info != gds_info_end) && !done;)
{
l = (USHORT) gds__vax_integer(info + 1, 2);
type = (USHORT) gds__vax_integer(info + 3, l);
switch (*info)
{
case gds_info_sql_stmt_type:
if (type == gds_info_sql_stmt_get_segment ||
type == gds_info_sql_stmt_put_segment)
{
ret = STMT_BLOB;
done = TRUE;
}
break;
case isc_info_sql_batch_fetch:
if (type == FALSE)
ret = STMT_NO_BATCH;
break;
case isc_info_error:
case isc_info_truncated:
done = TRUE;
break;
}
info += 3 + l;
}
}
THREAD_ENTER;
return ret;
}
STATUS port::compile(P_CMPL* compile, PACKET* send)
{
/**************************************
*
* c o m p i l e
*
**************************************
*
* Functional description
* Compile and request.
*
**************************************/
RDB rdb;
RRQ request;
MSG message, next;
UCHAR *blr;
USHORT max_msg, blr_length;
int *handle;
STATUS status_vector[20];
rrq::rrq_repeat* tail;
OBJCT object;
rdb = this->port_context;
handle = NULL;
blr = compile->p_cmpl_blr.cstr_address;
blr_length = compile->p_cmpl_blr.cstr_length;
THREAD_EXIT;
isc_compile_request(status_vector,
reinterpret_cast<void**>(GDS_REF(rdb->rdb_handle)),
reinterpret_cast<void**>(GDS_REF(handle)),
blr_length,
reinterpret_cast<char*>(GDS_VAL(blr)));
THREAD_ENTER;
if (status_vector[1])
return this->send_response(send, 0, 0, status_vector);
/* Parse the request to find the messages */
message = PARSE_messages(blr, blr_length);
max_msg = 0;
for (next = message; next; next = next->msg_next)
max_msg = MAX(max_msg, next->msg_number);
/* Allocate block and merge into data structures */
request = (RRQ) ALLOCV(type_rrq, max_msg + 1);
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("compile(server) allocate request %x\n", request);
#endif
request->rrq_handle = handle;
request->rrq_rdb = rdb;
request->rrq_max_msg = max_msg;
if (request->rrq_id = this->get_id(&request->rrq_header))
{
object = request->rrq_id;
request->rrq_next = rdb->rdb_requests;
rdb->rdb_requests = request;
}
else {
THREAD_EXIT;
isc_release_request(status_vector,
reinterpret_cast<void**>(GDS_REF(request->rrq_handle)));
THREAD_ENTER;
ALLR_release(request);
status_vector[0] = isc_arg_gds;
status_vector[1] = isc_too_many_handles;
status_vector[2] = isc_arg_end;
return this->send_response(send, 0, 0, status_vector);
}
while (message) {
next = message->msg_next;
message->msg_next = message;
#ifdef SCROLLABLE_CURSORS
message->msg_prior = message;
#endif
tail = request->rrq_rpt + message->msg_number;
tail->rrq_message = message;
tail->rrq_xdr = message;
tail->rrq_format = (FMT) message->msg_address;
message->msg_address = NULL;
message = next;
}
return this->send_response(send, object, 0, status_vector);
}
STATUS port::ddl(P_DDL* ddl, PACKET* send)
{
/**************************************
*
* d d l
*
**************************************
*
* Functional description
* Execute isc_ddl call.
*
**************************************/
STATUS status_vector[20];
UCHAR *blr;
USHORT blr_length;
RTR transaction;
RDB rdb;
CHECK_HANDLE_MEMBER(transaction,
RTR,
type_rtr,
ddl->p_ddl_transaction,
isc_bad_trans_handle);
rdb = this->port_context;
blr = ddl->p_ddl_blr.cstr_address;
blr_length = ddl->p_ddl_blr.cstr_length;
THREAD_EXIT;
isc_ddl(status_vector,
reinterpret_cast<void**>(GDS_REF(rdb->rdb_handle)),
reinterpret_cast<void**>(GDS_REF(transaction->rtr_handle)),
blr_length, reinterpret_cast<char*>(GDS_VAL(blr)));
THREAD_ENTER;
return this->send_response(send, 0, 0, status_vector);
}
void port::disconnect(PACKET* send, PACKET* receive)
{
/**************************************
*
* d i s c o n n e c t
*
**************************************
*
* Functional description
* We've lost the connection to the parent. Stop everything.
*
**************************************/
RTR transaction;
STATUS status_vector[20];
if (this->port_flags & PORT_async)
return;
this->port_flags |= PORT_disconnect;
RDB rdb = this->port_context;
if (!rdb) {
REMOTE_free_packet(this, send);
REMOTE_free_packet(this, receive);
this->disconnect();
return;
}
if (rdb->rdb_handle)
if (!(rdb->rdb_flags & RDB_service)) {
#ifdef CANCEL_OPERATION
/* Prevent a pending or spurious cancel from aborting
a good, clean detach from the database. */
THREAD_EXIT;
gds__cancel_operation(status_vector,
(struct hndl **) GDS_REF(rdb->rdb_handle),
CANCEL_disable);
THREAD_ENTER;
#endif
while (rdb->rdb_requests)
release_request(rdb->rdb_requests);
while (rdb->rdb_sql_requests)
release_sql_request(rdb->rdb_sql_requests);
while (transaction = rdb->rdb_transactions) {
THREAD_EXIT;
if (!(transaction->rtr_flags & RTR_limbo))
isc_rollback_transaction(status_vector,
reinterpret_cast<void**>(GDS_REF(transaction->rtr_handle)));
#ifdef SUPERSERVER
/* The underlying JRD subsystem will release all
memory resources related to a limbo transaction
as a side-effect of the database detach call
below. However, the y-valve handle must be released
as well since an isc_disconnect_transaction()
call doesn't exist. */
else
gds__handle_cleanup(status_vector,
(struct hndl **) GDS_REF(transaction->
rtr_handle));
#endif
THREAD_ENTER;
release_transaction(rdb->rdb_transactions);
}
THREAD_EXIT;
isc_detach_database(status_vector,
reinterpret_cast<void**>(GDS_REF(rdb->rdb_handle)));
THREAD_ENTER;
while (rdb->rdb_events) {
release_event(rdb->rdb_events);
}
if (this->port_statement) {
release_statement(&this->port_statement);
}
}
else
{
THREAD_EXIT;
isc_service_detach(status_vector,
reinterpret_cast<void**>(GDS_REF(rdb->rdb_handle)));
THREAD_ENTER;
}
REMOTE_free_packet(this, send);
REMOTE_free_packet(this, receive);
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("disconnect(server) free rdb %x\n", rdb);
#endif
this->port_context = NULL;
ALLR_release(rdb);
if (this->port_object_vector)
{
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("disconnect(server) free vector %x\n",
this->port_object_vector);
#endif
ALLR_release(this->port_object_vector);
this->port_object_vector = NULL;
}
if (this->port_connection)
{
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("disconnect(server) free string %x\n",
this->port_connection);
#endif
ALLR_release(this->port_connection);
this->port_connection = NULL;
}
if (this->port_version)
{
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("disconnect(server) free string %x\n",
this->port_version);
#endif
ALLR_release(this->port_version);
this->port_version = NULL;
}
if (this->port_passwd)
{
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("disconnect(server) free string %x\n",
this->port_passwd);
#endif
ALLR_release(this->port_passwd);
this->port_passwd = NULL;
}
if (this->port_user_name)
{
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("disconnect(server) free string %x\n",
this->port_user_name);
#endif
ALLR_release(this->port_user_name);
this->port_user_name = NULL;
}
if (this->port_host)
{
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("disconnect(server) free string %x\n",
this->port_host);
#endif
ALLR_release(this->port_host);
this->port_host = NULL;
}
this->disconnect();
}
void port::drop_database(P_RLSE* release, PACKET* send)
{
/**************************************
*
* d r o p _ d a t a b a s e
*
**************************************
*
* Functional description
* End a request.
*
**************************************/
RDB rdb;
STATUS status_vector[20];
rdb = this->port_context;
THREAD_EXIT;
isc_drop_database(status_vector,
reinterpret_cast<void**>(GDS_REF(rdb->rdb_handle)));
THREAD_ENTER;
if (status_vector[1]
&& (status_vector[1] != gds_drdb_completed_with_errs)) {
(void) this->send_response(send, 0, 0, status_vector);
return;
};
while (rdb->rdb_events)
release_event(rdb->rdb_events);
while (rdb->rdb_requests)
release_request(rdb->rdb_requests);
while (rdb->rdb_sql_requests)
release_sql_request(rdb->rdb_sql_requests);
while (rdb->rdb_transactions)
release_transaction(rdb->rdb_transactions);
if (this->port_statement)
release_statement(&this->port_statement);
this->send_response(send, 0, 0, status_vector);
}
#ifdef SCROLLABLE_CURSORS
static MSG dump_cache( rrq::rrq_repeat* tail)
{
/**************************************
*
* d u m p _ c a c h e
*
**************************************
*
* Functional description
* We have encountered a situation where what's in
* cache is not useful, so empty the cache in
* preparation for refilling it.
*
**************************************/
MSG message;
message = tail->rrq_xdr;
while (TRUE) {
message->msg_address = NULL;
message = message->msg_next;
if (message == tail->rrq_xdr)
break;
}
tail->rrq_message = message;
return message;
}
#endif
STATUS port::end_blob(P_OP operation, P_RLSE * release, PACKET* send)
{
/**************************************
*
* e n d _ b l o b
*
**************************************
*
* Functional description
* End a blob.
*
**************************************/
RBL blob;
STATUS status_vector[20];
CHECK_HANDLE_MEMBER(blob,
RBL,
type_rbl,
release->p_rlse_object,
isc_bad_segstr_handle);
THREAD_EXIT;
if (operation == op_close_blob)
isc_close_blob(status_vector,
reinterpret_cast<void**>(GDS_REF(blob->rbl_handle)));
else
isc_cancel_blob(status_vector,
reinterpret_cast<void**>(GDS_REF(blob->rbl_handle)));
THREAD_ENTER;
if (!status_vector[1]) {
release_blob(blob);
}
return this->send_response(send, 0, 0, status_vector);
}
STATUS port::end_database(P_RLSE * release, PACKET* send)
{
/**************************************
*
* e n d _ d a t a b a s e
*
**************************************
*
* Functional description
* End a request.
*
**************************************/
RDB rdb;
STATUS status_vector[20];
rdb = this->port_context;
THREAD_EXIT;
isc_detach_database(status_vector,
reinterpret_cast<void**>(GDS_REF(rdb->rdb_handle)));
THREAD_ENTER;
if (status_vector[1])
return this->send_response(send, 0, 0, status_vector);
while (rdb->rdb_events)
release_event(rdb->rdb_events);
while (rdb->rdb_requests)
release_request(rdb->rdb_requests);
while (rdb->rdb_sql_requests)
release_sql_request(rdb->rdb_sql_requests);
while (rdb->rdb_transactions)
release_transaction(rdb->rdb_transactions);
if (this->port_statement)
release_statement(&this->port_statement);
return this->send_response(send, 0, 0, status_vector);
}
STATUS port::end_request(P_RLSE * release, PACKET* send)
{
/**************************************
*
* e n d _ r e q u e s t
*
**************************************
*
* Functional description
* End a request.
*
**************************************/
RRQ request;
STATUS status_vector[20];
CHECK_HANDLE_MEMBER(request,
RRQ,
type_rrq,
release->p_rlse_object,
isc_bad_req_handle);
THREAD_EXIT;
isc_release_request(status_vector,
reinterpret_cast<void**>(GDS_REF(request->rrq_handle)));
THREAD_ENTER;
if (!status_vector[1])
release_request(request);
return this->send_response(send, 0, 0, status_vector);
}
STATUS port::end_statement(P_SQLFREE* free_stmt, PACKET* send)
{
/*****************************************
*
* e n d _ s t a t e m e n t
*
*****************************************
*
* Functional description
* Free a statement.
*
*****************************************/
RSR statement;
USHORT object;
STATUS status_vector[20];
CHECK_HANDLE_MEMBER(statement,
RSR,
type_rsr,
free_stmt->p_sqlfree_statement,
isc_bad_req_handle);
THREAD_EXIT;
GDS_DSQL_FREE(status_vector,
reinterpret_cast<void**>(&statement->rsr_handle),
free_stmt->p_sqlfree_option);
THREAD_ENTER;
if (status_vector[1])
return this->send_response(send, 0, 0, status_vector);
if (!statement->rsr_handle) {
release_sql_request(statement);
statement = NULL;
}
else {
statement->rsr_flags &= ~RSR_fetched;
statement->rsr_rtr = NULL;
REMOTE_reset_statement(statement);
statement->rsr_message = statement->rsr_buffer;
}
object = (statement) ? statement->rsr_id : (USHORT) - 1;
return this->send_response(send, object, 0, status_vector);
}
STATUS port::end_transaction(P_OP operation, P_RLSE * release, PACKET* send)
{
/**************************************
*
* e n d _ t r a n s a c t i o n
*
**************************************
*
* Functional description
* End a transaction.
*
**************************************/
RTR transaction;
STATUS status_vector[20];
CHECK_HANDLE_MEMBER(transaction,
RTR,
type_rtr,
release->p_rlse_object,
isc_bad_trans_handle);
THREAD_EXIT;
switch (operation)
{
case op_commit:
isc_commit_transaction(status_vector,
reinterpret_cast<void**>(GDS_REF(transaction->rtr_handle)));
break;
case op_rollback:
isc_rollback_transaction(status_vector,
reinterpret_cast<void**>(GDS_REF(transaction->rtr_handle)));
break;
case op_rollback_retaining:
isc_rollback_retaining(status_vector,
reinterpret_cast<void**>(GDS_REF(transaction->rtr_handle)));
break;
case op_commit_retaining:
isc_commit_retaining(status_vector,
reinterpret_cast<void**>(GDS_REF(transaction->rtr_handle)));
break;
case op_prepare:
if (!isc_prepare_transaction(status_vector,
reinterpret_cast<void**>(GDS_REF(transaction->rtr_handle))))
transaction->rtr_flags |= RTR_limbo;
break;
}
THREAD_ENTER;
if (!status_vector[1])
if (operation == op_commit || operation == op_rollback) {
REMOTE_cleanup_transaction(transaction);
release_transaction(transaction);
}
return this->send_response(send, 0, 0, status_vector);
}
STATUS port::execute_immediate(P_OP op, P_SQLST * exnow, PACKET* send)
{
/*****************************************
*
* e x e c u t e _ i m m e d i a t e
*
*****************************************
*
* Functional description
* process an execute immediate DSQL packet
*
*****************************************/
RDB rdb;
RTR transaction = (RTR) NULL;
USHORT in_blr_length, in_msg_type, in_msg_length, parser_version,
out_blr_length, out_msg_type, out_msg_length;
UCHAR *in_blr, *in_msg, *out_blr, *out_msg;
int *handle;
STATUS status_vector[20];
rdb = this->port_context;
/** Do not call CHECK_HANDLE if this is the start of a transaction **/
if (this->port_objects && exnow->p_sqlst_transaction) {
CHECK_HANDLE_MEMBER(transaction,
RTR,
type_rtr,
exnow->p_sqlst_transaction,
isc_bad_trans_handle);
}
in_msg_length = out_msg_length = 0;
in_msg = out_msg = NULL;
if (op == op_exec_immediate2)
{
in_blr_length = exnow->p_sqlst_blr.cstr_length;
in_blr = exnow->p_sqlst_blr.cstr_address;
in_msg_type = exnow->p_sqlst_message_number;
if (this->port_statement->rsr_bind_format)
{
in_msg_length = this->port_statement->rsr_bind_format->fmt_length;
in_msg = this->port_statement->rsr_message->msg_address;
}
out_blr_length = exnow->p_sqlst_out_blr.cstr_length;
out_blr = exnow->p_sqlst_out_blr.cstr_address;
out_msg_type = exnow->p_sqlst_out_message_number;
if (this->port_statement->rsr_select_format)
{
out_msg_length =
this->port_statement->rsr_select_format->fmt_length;
if (!this->port_statement->rsr_message->msg_address)
{
/* TMN: Obvious bugfix. Please look at your compilers warnings. */
/* They are not enemies, they're friends! */
/* port->port_statement->rsr_message->msg_address = &port->port_statement->rsr_message->msg_buffer; */
this->port_statement->rsr_message->msg_address =
this->port_statement->rsr_message->msg_buffer;
}
out_msg = this->port_statement->rsr_message->msg_address;
}
}
else
{
in_blr_length = out_blr_length = 0;
in_blr = out_blr = NULL;
in_msg_type = out_msg_type = 0;
}
handle = (transaction) ? transaction->rtr_handle : NULL;
/* Since the API to GDS_DSQL_EXECUTE_IMMED is public and can not be changed, there needs to
* be a way to send the parser version to DSQL so that the parser can compare the keyword
* version to the parser version. To accomplish this, the parser version is combined with
* the client dialect and sent across that way. In dsql8_execute_immediate, the parser version
* and client dialect are separated and passed on to their final desintations. The information
* is combined as follows:
* Dialect * 10 + parser_version
*
* and is extracted in dsql8_execute_immediate as follows:
* parser_version = ((dialect *10)+parser_version)%10
* client_dialect = ((dialect *10)+parser_version)/10
*
* For example, parser_version = 1 and client dialect = 1
*
* combined = (1 * 10) + 1 == 11
*
* parser = (combined) %10 == 1
* dialect = (combined) / 19 == 1
*/
parser_version = (this->port_protocol < PROTOCOL_VERSION10) ? 1 : 2;
THREAD_EXIT;
GDS_DSQL_EXECUTE_IMMED(status_vector,
reinterpret_cast<void**>(&rdb->rdb_handle),
reinterpret_cast<void**>(&handle),
exnow->p_sqlst_SQL_str.cstr_length,
reinterpret_cast<char*>(exnow->p_sqlst_SQL_str.cstr_address),
(USHORT) ((exnow->p_sqlst_SQL_dialect * 10) +
parser_version),
in_blr_length,
reinterpret_cast<char*>(in_blr),
in_msg_type,
in_msg_length,
reinterpret_cast<char*>(in_msg),
out_blr_length,
reinterpret_cast<char*>(out_blr),
out_msg_type,
out_msg_length,
reinterpret_cast<char*>(out_msg));
THREAD_ENTER;
if (op == op_exec_immediate2)
{
this->port_statement->rsr_format =
this->port_statement->rsr_select_format;
send->p_operation = op_sql_response;
send->p_sqldata.p_sqldata_messages = (status_vector[1]
|| !out_msg) ? 0 : 1;
this->send_partial(send);
}
if (!status_vector[1]) {
if (transaction && !handle) {
REMOTE_cleanup_transaction(transaction);
release_transaction(transaction);
transaction = NULL;
}
else if (!transaction && handle) {
if (!(transaction = make_transaction(rdb, handle))) {
status_vector[0] = isc_arg_gds;
status_vector[1] = isc_too_many_handles;
status_vector[2] = isc_arg_end;
}
}
}
return this->send_response( send,
(OBJCT) (transaction ? transaction->rtr_id : 0),
0,
status_vector);
}
STATUS port::execute_statement(P_OP op, P_SQLDATA* sqldata, PACKET* send)
{
/*****************************************
*
* e x e c u t e _ s t a t e m e n t
*
*****************************************
*
* Functional description
* Execute a non-SELECT dynamic SQL statement.
*
*****************************************/
RTR transaction = (RTR) NULL;
RSR statement;
USHORT in_msg_length, out_msg_type, out_blr_length, out_msg_length;
UCHAR *in_msg, *out_blr, *out_msg;
int *handle;
STATUS status_vector[20];
/** Do not call CHECK_HANDLE if this is the start of a transaction **/
if (sqldata->p_sqldata_transaction)
{
CHECK_HANDLE_MEMBER(transaction,
RTR,
type_rtr,
sqldata->p_sqldata_transaction,
isc_bad_trans_handle);
}
CHECK_HANDLE_MEMBER(statement,
RSR,
type_rsr,
sqldata->p_sqldata_statement,
isc_bad_req_handle);
in_msg_length = out_msg_length = 0;
in_msg = out_msg = NULL;
if (statement->rsr_format)
{
in_msg_length = statement->rsr_format->fmt_length;
in_msg = statement->rsr_message->msg_address;
}
if (op == op_execute2)
{
out_blr_length = sqldata->p_sqldata_out_blr.cstr_length;
out_blr = sqldata->p_sqldata_out_blr.cstr_address;
out_msg_type = sqldata->p_sqldata_out_message_number;
if (this->port_statement->rsr_select_format)
{
out_msg_length =
this->port_statement->rsr_select_format->fmt_length;
out_msg = this->port_statement->rsr_message->msg_buffer;
}
}
else
{
out_blr_length = 0;
out_msg_type = 0;
out_blr = NULL;
}
statement->rsr_flags &= ~RSR_fetched;
handle = (transaction) ? transaction->rtr_handle : NULL;
THREAD_EXIT;
GDS_DSQL_EXECUTE(status_vector,
reinterpret_cast<void**>(&handle),
reinterpret_cast<void**>(&statement->rsr_handle),
sqldata->p_sqldata_blr.cstr_length,
reinterpret_cast<char*>(sqldata->p_sqldata_blr.cstr_address),
sqldata->p_sqldata_message_number,
in_msg_length,
reinterpret_cast<char*>(in_msg),
out_blr_length,
reinterpret_cast<char*>(out_blr),
out_msg_type,
out_msg_length,
reinterpret_cast<char*>(out_msg));
THREAD_ENTER;
if (op == op_execute2)
{
this->port_statement->rsr_format =
this->port_statement->rsr_select_format;
send->p_operation = op_sql_response;
send->p_sqldata.p_sqldata_messages = (status_vector[1]
|| !out_msg) ? 0 : 1;
this->send_partial(send);
}
if (!status_vector[1]) {
if (transaction && !handle) {
REMOTE_cleanup_transaction(transaction);
release_transaction(transaction);
transaction = NULL;
}
else if (!transaction && handle) {
if (!(transaction = make_transaction(statement->rsr_rdb, handle))) {
status_vector[0] = isc_arg_gds;
status_vector[1] = isc_too_many_handles;
status_vector[2] = isc_arg_end;
}
}
statement->rsr_rtr = transaction;
}
return this->send_response( send,
(OBJCT) (transaction ? transaction->rtr_id : 0),
0,
status_vector);
}
STATUS port::fetch(P_SQLDATA * sqldata, PACKET* send)
{
/*****************************************
*
* f e t c h
*
*****************************************
*
* Functional description
* Fetch next record from a dynamic SQL cursor.
*
*****************************************/
RSR statement;
MSG message, next;
USHORT msg_length, count, count2;
P_SQLDATA *response;
STATUS s, status_vector[20];
CHECK_HANDLE_MEMBER(statement,
RSR,
type_rsr,
sqldata->p_sqldata_statement,
isc_bad_req_handle);
if (statement->rsr_flags & RSR_blob) {
return this->fetch_blob(sqldata, send);
}
if (statement->rsr_format) {
msg_length = statement->rsr_format->fmt_length;
} else {
msg_length = 0;
}
count = ((this->port_flags & PORT_rpc) ||
(statement->rsr_flags & RSR_no_batch)) ? 1 :
sqldata->p_sqldata_messages;
count2 = (statement->rsr_flags & RSR_no_batch) ? 0 : count;
/* On first fetch, clear the end-of-stream flag & reset the message buffers */
if (!(statement->rsr_flags & RSR_fetched)) {
statement->rsr_flags &= ~(RSR_eof | RSR_stream_err);
memset(statement->rsr_status_vector, 0,
sizeof(statement->rsr_status_vector));
if ((message = statement->rsr_message) != NULL) {
statement->rsr_buffer = message;
while (TRUE) {
message->msg_address = NULL;
message = message->msg_next;
if (message == statement->rsr_message)
break;
}
}
}
/* Get ready to ship the data out */
response = &send->p_sqldata;
send->p_operation = op_fetch_response;
response->p_sqldata_statement = sqldata->p_sqldata_statement;
response->p_sqldata_status = 0;
response->p_sqldata_messages = 1;
s = 0;
message = NULL;
/* Check to see if any messages are already sitting around */
while (TRUE) {
/* Have we exhausted the cache & reached cursor EOF? */
if ((statement->rsr_flags & RSR_eof) && !statement->rsr_msgs_waiting) {
statement->rsr_flags &= ~RSR_eof;
s = 100;
count2 = 0;
break;
}
/* Have we exhausted the cache & have a pending error? */
if ((statement->rsr_flags & RSR_stream_err)
&& !statement->rsr_msgs_waiting) {
statement->rsr_flags &= ~RSR_stream_err;
return this->send_response(send, 0, 0,
statement->rsr_status_vector);
}
message = statement->rsr_buffer;
/* Make sure message can be de referenced, if not then return false */
if (message == NULL)
return FALSE;
/* If we don't have a message cached, get one from the
access method. */
if (!message->msg_address) {
assert(statement->rsr_msgs_waiting == 0);
THREAD_EXIT;
s = GDS_DSQL_FETCH(status_vector,
reinterpret_cast<void**>(&statement->rsr_handle),
sqldata->p_sqldata_blr.cstr_length,
reinterpret_cast<char*>(sqldata->p_sqldata_blr.cstr_address),
sqldata->p_sqldata_message_number,
msg_length,
reinterpret_cast<char*>(message->msg_buffer));
THREAD_ENTER;
statement->rsr_flags |= RSR_fetched;
if (s) {
if (s == 100 || s == 101) {
count2 = 0;
break;
} else {
return this->send_response(send, 0, 0, status_vector);
}
}
message->msg_address = message->msg_buffer;
}
else {
/* Take a message from the outqoing queue */
assert(statement->rsr_msgs_waiting >= 1);
statement->rsr_msgs_waiting--;
}
/* For compatibility with Protocol 7, we must break out of the
loop before sending the last record. */
count--;
if (this->port_protocol <= PROTOCOL_VERSION7 && count <= 0) {
break;
}
/* There's a buffer waiting -- send it */
if (!this->send_partial(send)) {
return FALSE;
}
message->msg_address = NULL;
/* If we've sent the requested amount, break out of loop */
if (count <= 0)
break;
}
response->p_sqldata_status = s;
response->p_sqldata_messages = 0;
this->send(send);
if (message) {
message->msg_address = NULL;
}
/* Since we have a little time on our hands while this packet is sent
and processed, get the next batch of records. Start by finding the
next free buffer. */
message = statement->rsr_buffer;
next = NULL;
while (message->msg_address && message->msg_next != statement->rsr_buffer)
message = message->msg_next;
for (; count2; --count2) {
if (message->msg_address) {
if (!next)
for (next = statement->rsr_buffer; next->msg_next != message;
next = next->msg_next);
message = (MSG) ALLOCV(type_msg, statement->rsr_fmt_length);
message->msg_number = next->msg_number;
message->msg_next = next->msg_next;
next->msg_next = message;
next = message;
}
THREAD_EXIT;
s = GDS_DSQL_FETCH(status_vector,
reinterpret_cast<void**>(&statement->rsr_handle),
sqldata->p_sqldata_blr.cstr_length,
reinterpret_cast<char*>(sqldata->p_sqldata_blr.cstr_address),
sqldata->p_sqldata_message_number,
msg_length,
reinterpret_cast<char*>(message->msg_buffer));
THREAD_ENTER;
if (s) {
if (status_vector[1]) {
/* If already have an error queued, don't overwrite it */
if (!(statement->rsr_flags & RSR_stream_err)) {
statement->rsr_flags |= RSR_stream_err;
memcpy(statement->rsr_status_vector, status_vector,
sizeof(statement->rsr_status_vector));
}
}
if (s == 100)
statement->rsr_flags |= RSR_eof;
break;
}
message->msg_address = message->msg_buffer;
message = message->msg_next;
statement->rsr_msgs_waiting++;
}
return TRUE;
}
STATUS port::fetch_blob(P_SQLDATA * sqldata, PACKET* send)
{
/*****************************************
*
* f e t c h _ b l o b
*
*****************************************
*
* Functional description
* Fetch next record from a dynamic SQL cursor.
*
*****************************************/
RSR statement;
USHORT msg_length;
MSG message;
P_SQLDATA *response;
STATUS s, status_vector[20];
CHECK_HANDLE_MEMBER(statement,
RSR,
type_rsr,
sqldata->p_sqldata_statement,
isc_bad_req_handle);
if (statement->rsr_format)
msg_length = statement->rsr_format->fmt_length;
else
msg_length = 0;
if ((message = statement->rsr_message) != NULL)
statement->rsr_buffer = message;
/* Get ready to ship the data out */
response = &send->p_sqldata;
send->p_operation = op_fetch_response;
response->p_sqldata_statement = sqldata->p_sqldata_statement;
response->p_sqldata_status = 0;
response->p_sqldata_messages = 1;
s = 0;
message = statement->rsr_buffer;
THREAD_EXIT;
s = GDS_DSQL_FETCH(status_vector,
reinterpret_cast<void**>(&statement->rsr_handle),
sqldata->p_sqldata_blr.cstr_length,
reinterpret_cast<char*>(sqldata->p_sqldata_blr.cstr_address),
sqldata->p_sqldata_message_number,
msg_length,
reinterpret_cast<char*>(message->msg_buffer));
THREAD_ENTER;
if (!status_vector[1] ||
status_vector[1] != gds_segment || status_vector[1] != gds_segstr_eof) {
message->msg_address = message->msg_buffer;
response->p_sqldata_status = s;
response->p_sqldata_messages =
(status_vector[1] == gds_segstr_eof) ? 0 : 1;
this->send_partial(send);
message->msg_address = NULL;
}
return this->send_response(send, 0, 0, status_vector);
}
OBJCT port::get_id(BLK block)
{
/**************************************
*
* g e t _ i d
*
**************************************
*
* Functional description
* Allocate an object slot for an object.
* If the object vector cannot be expanded
* to accomodate the object slot then
* REMOTE_set_object() will return a NULL
* object slot.
*
**************************************/
/* If there isn't a vector, obvious the object goes in slot 1.
Reserve slot 0 so we can distinguish something from nothing.
NOTE: prior to server version 4.5.0 id==0 COULD be used - so
only the server side can now depend on id==0 meaning "invalid id" */
VEC vector = this->port_object_vector;
if (!vector) {
return REMOTE_set_object(this, block, (OBJCT) 1);
}
// Search vector for an empty slot. If we find one, use it
BLK *p, *end;
for (p = vector->vec_object + 1, end =
vector->vec_object + vector->vec_count; p < end; p++)
{
if (!*p) {
*p = block;
return (OBJCT) (p - vector->vec_object);
}
}
/* Vector is full -- somebody will need to expand it */
return REMOTE_set_object(this, block, (OBJCT) vector->vec_count);
}
static USHORT get_next_msg_no(RRQ request,
USHORT incarnation,
USHORT * msg_number)
{
/**************************************
*
* g e t _ n e x t _ m s g _ n o
*
**************************************
*
* Functional description
* Return the number of the next message
* in the request.
*
**************************************/
USHORT l, n, result;
STATUS status_vector[20];
UCHAR info_buffer[128], *info;
THREAD_EXIT;
isc_request_info(status_vector,
reinterpret_cast<void**>(GDS_REF(request->rrq_handle)),
incarnation,
sizeof(request_info),
(SCHAR *) request_info, /* const_cast */
sizeof(info_buffer),
reinterpret_cast<char*>(info_buffer));
THREAD_ENTER;
if (status_vector[1])
return FALSE;
result = FALSE;
for (info = info_buffer; *info != gds_info_end;) {
l = (USHORT) gds__vax_integer(info + 1, 2);
n = (USHORT) gds__vax_integer(info + 3, l);
switch (*info) {
case gds_info_state:
if (n != gds_info_req_send)
return FALSE;
break;
case gds_info_message_number:
*msg_number = n;
result = TRUE;
break;
default:
return FALSE;
}
info += 3 + l;
}
return result;
}
STATUS port::get_segment(P_SGMT* segment, PACKET* send)
{
/**************************************
*
* g e t _ s e g m e n t
*
**************************************
*
* Functional description
* Get a single blob segment.
*
**************************************/
RBL blob;
UCHAR *buffer;
USHORT length, buffer_length;
UCHAR *p;
STATUS state, status, status_vector[20];
#ifndef STACK_EFFICIENT
UCHAR temp_buffer[BLOB_LENGTH];
#endif
CHECK_HANDLE_MEMBER(blob,
RBL,
type_rbl,
segment->p_sgmt_blob,
isc_bad_segstr_handle);
buffer_length = segment->p_sgmt_length;
#ifndef STACK_EFFICIENT
if (buffer_length <= sizeof(temp_buffer))
buffer = temp_buffer;
else
#endif
{
if (buffer_length > blob->rbl_buffer_length) {
if (blob->rbl_buffer != blob->rbl_data) {
ALLR_free(blob->rbl_buffer);
blob->rbl_buffer = blob->rbl_data;
blob->rbl_buffer_length = 1;
}
blob->rbl_buffer = ALLR_alloc((SLONG) buffer_length);
blob->rbl_buffer_length = buffer_length;
}
buffer = blob->rbl_buffer;
}
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("get_segment(server) allocate buffer %x\n", buffer);
#endif
send->p_resp.p_resp_data.cstr_address = buffer;
/* Be backwards compatible */
if (this->port_flags & PORT_rpc)
{
length = 0;
THREAD_EXIT;
isc_get_segment(status_vector,
reinterpret_cast<void**>(GDS_REF(blob->rbl_handle)),
GDS_REF(length),
segment->p_sgmt_length,
reinterpret_cast<char*>(GDS_VAL(buffer)));
THREAD_ENTER;
status =
this->send_response(send, blob->rbl_id, length, status_vector);
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("get_segment(server) free buffer %x\n", buffer);
#endif
if (status_vector[1] == gds_segstr_eof)
if (blob->rbl_buffer != blob->rbl_data) {
ALLR_free(blob->rbl_buffer);
blob->rbl_buffer = blob->rbl_data;
blob->rbl_buffer_length = 1;
}
return status;
}
/* Gobble up a buffer's worth of segments */
p = buffer;
state = 0;
while (buffer_length > 2) {
buffer_length -= 2;
p += 2;
THREAD_EXIT;
isc_get_segment(status_vector,
reinterpret_cast<void**>(GDS_REF(blob->rbl_handle)),
GDS_REF(length),
buffer_length,
reinterpret_cast<char*>(GDS_VAL(p)));
THREAD_ENTER;
if (status_vector[1] == gds_segstr_eof)
{
state = 2;
success(status_vector);
p -= 2;
break;
}
if (status_vector[1] && (status_vector[1] != gds_segment))
{
p -= 2;
break;
}
p[-2] = (UCHAR) length;
p[-1] = (UCHAR) (length >> 8);
p += length;
buffer_length -= length;
if (status_vector[1] == gds_segment) {
state = 1;
success(status_vector);
break;
}
}
status = this->send_response(send,
(OBJCT)state,
(USHORT) (p - buffer),
status_vector);
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("get_segment(server) free buffer %x\n", buffer);
#endif
if (status_vector[1] == gds_segstr_eof)
if (blob->rbl_buffer != blob->rbl_data) {
ALLR_free(blob->rbl_buffer);
blob->rbl_buffer = blob->rbl_data;
blob->rbl_buffer_length = 1;
}
return status;
}
STATUS port::get_slice(P_SLC * stuff, PACKET* send)
{
/**************************************
*
* g e t _ s l i c e
*
**************************************
*
* Functional description
* Get an array slice.
*
**************************************/
RDB rdb;
RTR transaction;
UCHAR *slice;
P_SLR *response;
STATUS status, status_vector[20];
#ifndef STACK_EFFICIENT
UCHAR temp_buffer[4096];
#endif
rdb = this->port_context;
CHECK_HANDLE_MEMBER(transaction,
RTR,
type_rtr,
stuff->p_slc_transaction,
isc_bad_trans_handle);
if (!stuff->p_slc_length)
slice = 0;
else {
#ifndef STACK_EFFICIENT
if (stuff->p_slc_length <= sizeof(temp_buffer))
slice = temp_buffer;
else
#endif
slice = ALLR_alloc((SLONG) stuff->p_slc_length);
}
if (slice) {
memset(slice, 0, stuff->p_slc_length);
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("get_slice(server) allocate buffer %x\n", slice);
#endif
}
response = &send->p_slr;
THREAD_EXIT;
isc_get_slice(status_vector,
reinterpret_cast<void**>(GDS_REF(rdb->rdb_handle)),
reinterpret_cast<void**>(GDS_REF(transaction->rtr_handle)),
(GDS_QUAD *) GDS_REF(stuff->p_slc_id),
stuff->p_slc_sdl.cstr_length,
reinterpret_cast<char*>(GDS_VAL(stuff->p_slc_sdl.cstr_address)),
stuff->p_slc_parameters.cstr_length,
(ISC_LONG *) GDS_VAL(stuff->p_slc_parameters.cstr_address),
stuff->p_slc_length,
GDS_VAL(slice),
reinterpret_cast<long*>(GDS_REF(response->p_slr_length)));
THREAD_ENTER;
if (status_vector[1])
status = this->send_response(send, 0, 0, status_vector);
else {
send->p_operation = op_slice;
response->p_slr_slice.lstr_address = slice;
response->p_slr_slice.lstr_length = response->p_slr_length;
response->p_slr_sdl = stuff->p_slc_sdl.cstr_address;
response->p_slr_sdl_length = stuff->p_slc_sdl.cstr_length;
this->send(send);
response->p_slr_sdl = 0;
status = SUCCESS;
}
if (slice) {
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("get_slice(server) free buffer %x\n", slice);
#endif
#ifndef STACK_EFFICIENT
if (slice != temp_buffer)
#endif
ALLR_free(slice);
}
return status;
}
STATUS port::info(P_OP op, P_INFO * stuff, PACKET* send)
{
/**************************************
*
* i n f o
*
**************************************
*
* Functional description
* Get info for a blob, database, request, service,
* statement, or transaction.
*
**************************************/
RDB rdb;
RBL blob;
RTR transaction;
RRQ request;
RSR statement;
UCHAR *buffer, temp[1024];
TEXT version[64];
STATUS status, status_vector[20];
rdb = this->port_context;
/* Make sure there is a suitable temporary blob buffer */
buffer = ALLR_alloc((SLONG) stuff->p_info_buffer_length);
memset(buffer, 0, stuff->p_info_buffer_length);
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("info(server) allocate buffer %x\n", buffer);
#endif
switch (op) {
case op_info_blob:
CHECK_HANDLE_MEMBER(blob,
RBL,
type_rbl,
stuff->p_info_object,
isc_bad_segstr_handle);
THREAD_EXIT;
isc_blob_info(status_vector,
reinterpret_cast<void**>(GDS_REF(blob->rbl_handle)),
stuff->p_info_items.cstr_length,
reinterpret_cast<char*>(GDS_VAL(stuff->p_info_items.cstr_address)),
stuff->p_info_buffer_length,
reinterpret_cast<char*>(GDS_VAL(buffer)));
THREAD_ENTER;
break;
case op_info_database:
THREAD_EXIT;
isc_database_info(status_vector,
reinterpret_cast<void**>(GDS_REF(rdb->rdb_handle)),
stuff->p_info_items.cstr_length,
reinterpret_cast<char*>(GDS_VAL(stuff->p_info_items.cstr_address)),
sizeof(temp),
reinterpret_cast<char*>(temp));
if (!status_vector[1]) {
sprintf(version, "%s/%s", GDS_VERSION,
this->port_version->str_data);
MERGE_database_info(temp, buffer, stuff->p_info_buffer_length,
IMPLEMENTATION, 4, 1,
reinterpret_cast<UCHAR*>(version),
reinterpret_cast<UCHAR*>(this->port_host->str_data),
0);
}
THREAD_ENTER;
break;
case op_info_request:
CHECK_HANDLE_MEMBER(request,
RRQ,
type_rrq,
stuff->p_info_object,
isc_bad_req_handle);
THREAD_EXIT;
isc_request_info(status_vector,
reinterpret_cast <
void **>(GDS_REF(request->rrq_handle)),
stuff->p_info_incarnation,
stuff->p_info_items.cstr_length,
reinterpret_cast <
char *>(GDS_VAL(stuff->p_info_items.cstr_address)),
stuff->p_info_buffer_length,
reinterpret_cast < char *>(GDS_VAL(buffer)));
THREAD_ENTER;
break;
case op_info_transaction:
CHECK_HANDLE_MEMBER(transaction,
RTR,
type_rtr,
stuff->p_info_object,
isc_bad_trans_handle);
THREAD_EXIT;
isc_transaction_info(status_vector,
reinterpret_cast <
void **>(GDS_REF(transaction->rtr_handle)),
stuff->p_info_items.cstr_length,
reinterpret_cast <
char
*>(GDS_VAL(stuff->p_info_items.cstr_address)),
stuff->p_info_buffer_length,
reinterpret_cast < char *>(GDS_VAL(buffer)));
THREAD_ENTER;
break;
case op_service_info:
THREAD_EXIT;
isc_service_query(status_vector,
reinterpret_cast<void**>(&rdb->rdb_handle),
NULL,
stuff->p_info_items.cstr_length,
reinterpret_cast <
char *>(stuff->p_info_items.cstr_address),
stuff->p_info_recv_items.cstr_length,
reinterpret_cast <
char *>(stuff->p_info_recv_items.cstr_address),
stuff->p_info_buffer_length,
reinterpret_cast < char *>(buffer));
THREAD_ENTER;
break;
case op_info_sql:
CHECK_HANDLE_MEMBER(statement,
RSR,
type_rsr,
stuff->p_info_object,
isc_bad_req_handle);
THREAD_EXIT;
GDS_DSQL_SQL_INFO(status_vector,
reinterpret_cast<void**>(&statement->rsr_handle),
stuff->p_info_items.cstr_length,
reinterpret_cast <
char *>(stuff->p_info_items.cstr_address),
stuff->p_info_buffer_length,
reinterpret_cast < char *>(buffer));
THREAD_ENTER;
break;
}
/* Send a response that includes the segment. */
send->p_resp.p_resp_data.cstr_address = buffer;
status = this->send_response(send, stuff->p_info_object,
stuff->p_info_buffer_length, status_vector);
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("info(server) free buffer %x\n", buffer);
#endif
ALLR_free(buffer);
return status;
}
STATUS port::insert(P_SQLDATA * sqldata, PACKET* send)
{
/*****************************************
*
* i n s e r t
*
*****************************************
*
* Functional description
* Insert next record into a dynamic SQL cursor.
*
*****************************************/
RSR statement;
USHORT msg_length;
UCHAR *msg;
STATUS status_vector[20];
CHECK_HANDLE_MEMBER(statement,
RSR,
type_rsr,
sqldata->p_sqldata_statement,
isc_bad_req_handle);
if (statement->rsr_format)
{
msg_length = statement->rsr_format->fmt_length;
msg = statement->rsr_message->msg_address;
}
else
{
msg_length = 0;
msg = NULL;
}
THREAD_EXIT;
GDS_DSQL_INSERT(status_vector,
reinterpret_cast<void**>(&statement->rsr_handle),
sqldata->p_sqldata_blr.cstr_length,
reinterpret_cast <
char *>(sqldata->p_sqldata_blr.cstr_address),
sqldata->p_sqldata_message_number, msg_length,
reinterpret_cast < char *>(msg));
THREAD_ENTER;
return this->send_response(send, 0, 0, status_vector);
}
static RTR make_transaction( RDB rdb, int *handle)
{
/**************************************
*
* m a k e _ t r a n s a c t i o n
*
**************************************
*
* Functional description
* Create a local transaction handle.
*
**************************************/
RTR transaction;
transaction = (RTR) ALLOC(type_rtr);
transaction->rtr_rdb = rdb;
transaction->rtr_handle = handle;
if (transaction->rtr_id = rdb->rdb_port->get_id(&transaction->rtr_header))
{
transaction->rtr_next = rdb->rdb_transactions;
rdb->rdb_transactions = transaction;
}
else {
ALLR_release(transaction);
transaction = (RTR) 0;
}
return transaction;
}
STATUS port::open_blob(P_OP op, P_BLOB * stuff, PACKET* send)
{
/**************************************
*
* o p e n _ b l o b
*
**************************************
*
* Functional description
* Open or create a new blob.
*
**************************************/
RDB rdb;
RBL blob;
RTR transaction;
USHORT bpb_length;
UCHAR *bpb;
int *handle;
USHORT object;
STATUS status_vector[20];
CHECK_HANDLE_MEMBER(transaction,
RTR,
type_rtr,
stuff->p_blob_transaction,
isc_bad_trans_handle);
rdb = this->port_context;
handle = NULL;
bpb_length = 0;
bpb = NULL;
if (op == op_open_blob2 || op == op_create_blob2) {
bpb_length = stuff->p_blob_bpb.cstr_length;
bpb = stuff->p_blob_bpb.cstr_address;
}
THREAD_EXIT;
if (op == op_open_blob || op == op_open_blob2)
isc_open_blob2(status_vector,
reinterpret_cast<void**>(GDS_REF(rdb->rdb_handle)),
reinterpret_cast <
void **>(GDS_REF(transaction->rtr_handle)),
reinterpret_cast<void**>(GDS_REF(handle)),
(GDS_QUAD *) GDS_REF(stuff->p_blob_id), bpb_length,
GDS_VAL(bpb));
else
isc_create_blob2(status_vector,
reinterpret_cast <
void **>(GDS_REF(rdb->rdb_handle)),
reinterpret_cast <
void **>(GDS_REF(transaction->rtr_handle)),
reinterpret_cast<void**>(GDS_REF(handle)),
(GDS_QUAD *) GDS_REF(send->p_resp.p_resp_blob_id),
bpb_length,
reinterpret_cast < char *>(GDS_VAL(bpb)));
THREAD_ENTER;
if (status_vector[1])
object = 0;
else {
blob = (RBL) ALLOCV(type_rbl, 1);
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("open_blob(server) allocate blob %x\n", blob);
#endif
blob->rbl_buffer_length = 1;
blob->rbl_buffer = blob->rbl_data;
blob->rbl_handle = handle;
blob->rbl_rdb = rdb;
if (blob->rbl_id = this->get_id(&blob->rbl_header))
{
object = blob->rbl_id;
blob->rbl_rtr = transaction;
blob->rbl_next = transaction->rtr_blobs;
transaction->rtr_blobs = blob;
}
else
{
object = 0;
THREAD_EXIT;
isc_cancel_blob(status_vector,
reinterpret_cast <
void **>(GDS_REF(blob->rbl_handle)));
THREAD_ENTER;
ALLR_release(blob);
status_vector[0] = isc_arg_gds;
status_vector[1] = isc_too_many_handles;
status_vector[2] = isc_arg_end;
}
}
return this->send_response(send, object, 0, status_vector);
}
STATUS port::prepare(P_PREP * stuff, PACKET* send)
{
/**************************************
*
* p r e p a r e
*
**************************************
*
* Functional description
* End a transaction.
*
**************************************/
RTR transaction;
STATUS status_vector[20];
CHECK_HANDLE_MEMBER(transaction,
RTR,
type_rtr,
stuff->p_prep_transaction,
isc_bad_trans_handle);
THREAD_EXIT;
if (!isc_prepare_transaction2(status_vector,
reinterpret_cast<void**>(GDS_REF(transaction->rtr_handle)),
stuff->p_prep_data.cstr_length,
GDS_VAL(stuff->p_prep_data.cstr_address)))
{
transaction->rtr_flags |= RTR_limbo;
}
THREAD_ENTER;
return this->send_response(send, 0, 0, status_vector);
}
STATUS port::prepare_statement(P_SQLST * prepare, PACKET* send)
{
/*****************************************
*
* p r e p a r e _ s t a t m e n t
*
*****************************************
*
* Functional description
* Prepare a dynamic SQL statement for execution.
*
*****************************************/
RTR transaction = (RTR) NULL;
RSR statement;
UCHAR *buffer, local_buffer[1024];
STATUS status, status_vector[20];
USHORT state, parser_version;
int *handle;
/** Do not call CHECK_HANDLE if this is the start of a transaction **/
if (prepare->p_sqlst_transaction)
{
CHECK_HANDLE_MEMBER(transaction,
RTR,
type_rtr,
prepare->p_sqlst_transaction,
isc_bad_trans_handle);
}
CHECK_HANDLE_MEMBER(statement,
RSR,
type_rsr,
prepare->p_sqlst_statement,
isc_bad_req_handle);
if (prepare->p_sqlst_buffer_length > sizeof(local_buffer))
buffer = ALLR_alloc((SLONG) prepare->p_sqlst_buffer_length);
else
buffer = local_buffer;
handle = (transaction) ? transaction->rtr_handle : NULL;
/* Since the API to GDS_DSQL_PREPARE is public and can not be changed, there needs to
* be a way to send the parser version to DSQL so that the parser can compare the keyword
* version to the parser version. To accomplish this, the parser version is combined with
* the client dialect and sent across that way. In dsql8_prepare_statement, the parser version
* and client dialect are separated and passed on to their final desintations. The information
* is combined as follows:
* Dialect * 10 + parser_version
*
* and is extracted in dsql8_prepare_statement as follows:
* parser_version = ((dialect *10)+parser_version)%10
* client_dialect = ((dialect *10)+parser_version)/10
*
* For example, parser_version = 1 and client dialect = 1
*
* combined = (1 * 10) + 1 == 11
*
* parser = (combined) %10 == 1
* dialect = (combined) / 19 == 1
*/
parser_version = (this->port_protocol < PROTOCOL_VERSION10) ? 1 : 2;
THREAD_EXIT;
GDS_DSQL_PREPARE(status_vector,
reinterpret_cast<void**>(&handle),
reinterpret_cast<void**>(&statement->rsr_handle),
prepare->p_sqlst_SQL_str.cstr_length,
reinterpret_cast <
char *>(prepare->p_sqlst_SQL_str.cstr_address),
(USHORT) ((prepare->p_sqlst_SQL_dialect * 10) +
parser_version),
prepare->p_sqlst_items.cstr_length,
reinterpret_cast <
char *>(prepare->p_sqlst_items.cstr_address),
prepare->p_sqlst_buffer_length,
reinterpret_cast < char *>(buffer));
THREAD_ENTER;
if (status_vector[1]) {
if (buffer != local_buffer)
ALLR_free(buffer);
return this->send_response(send, 0, 0, status_vector);
}
REMOTE_reset_statement(statement);
statement->rsr_flags &= ~(RSR_blob | RSR_no_batch);
state = check_statement_type(statement);
if (state == STMT_BLOB)
statement->rsr_flags |= RSR_blob;
else if (state == STMT_NO_BATCH)
statement->rsr_flags |= RSR_no_batch;
state = (state == STMT_BLOB) ? 1 : 0;
/* Send a response that includes the info requested. */
send->p_resp.p_resp_data.cstr_address = buffer;
status =
this->send_response(send,
state,
prepare->p_sqlst_buffer_length,
status_vector);
if (buffer != local_buffer) {
ALLR_free(buffer);
}
return status;
}
BOOLEAN process_packet(PORT port,
PACKET* send, PACKET* receive, PORT * result)
{
/**************************************
*
* p r o c e s s _ p a c k e t
*
**************************************
*
* Functional description
* Given an packet received from client, process it a packet ready to be
* sent.
*
**************************************/
P_OP op;
STR string;
TEXT msg[128];
SRVR server;
struct trdb thd_context, *trdb;
trdb = &thd_context;
trdb->trdb_status_vector = port->port_status_vector;
THD_put_specific((THDD) trdb);
trdb->trdb_thd_data.thdd_type = THDD_TYPE_TRDB;
try {
switch (op = receive->p_operation)
{
case op_connect:
if (!accept_connection(port, &receive->p_cnct, send)) {
if (string = port->port_user_name) {
sprintf(msg,
"SERVER/process_packet: connection rejected for %*.*s",
string->str_length, string->str_length,
string->str_data);
gds__log(msg, 0);
}
if (port->port_server->srvr_flags & SRVR_multi_client)
port->port_state = state_broken;
else if (port->port_server->srvr_flags & SRVR_mpexl)
port->port_state = state_pending;
else {
gds__log
("SERVER/process_packet: connect reject, server exiting",
0);
THD_restore_specific();
return FALSE;
}
}
break;
case op_compile:
port->compile(&receive->p_cmpl, send);
break;
case op_attach:
case op_create:
attach_database(port, op, &receive->p_atch, send);
break;
case op_service_attach:
port->service_attach(&receive->p_atch, send);
break;
case op_service_start:
port->service_start(&receive->p_info, send);
break;
case op_disconnect:
case op_exit:
if (!(server = port->port_server))
break;
if ((server->srvr_flags & SRVR_multi_client) &&
port != server->srvr_parent_port) {
#ifdef XNET
if (port->port_type == port_xnet) {
port->disconnect(send, receive);
THD_restore_specific();
return FALSE;
}
#endif /* XNET */
port->disconnect(send, receive);
port = NULL;
break;
}
else {
if ((server->srvr_flags & SRVR_multi_client) &&
port == server->srvr_parent_port)
gds__log
("SERVER/process_packet: Multi-client server shutdown",
0);
port->disconnect(send, receive);
THD_restore_specific();
return FALSE;
}
case op_receive:
port->receive_msg(&receive->p_data, send);
break;
case op_send:
port->send_msg(&receive->p_data, send);
break;
case op_start:
case op_start_and_receive:
port->start(op, &receive->p_data, send);
break;
case op_start_and_send:
case op_start_send_and_receive:
port->start_and_send(op, &receive->p_data, send);
break;
case op_transact:
port->transact_request(&receive->p_trrq, send);
break;
case op_reconnect:
case op_transaction:
port->start_transaction(op, &receive->p_sttr, send);
break;
case op_prepare:
case op_rollback:
case op_rollback_retaining:
case op_commit:
case op_commit_retaining:
port->end_transaction(op, &receive->p_rlse, send);
break;
case op_detach:
port->end_database(&receive->p_rlse, send);
break;
case op_service_detach:
port->service_end(&receive->p_rlse, send);
break;
case op_drop_database:
port->drop_database(&receive->p_rlse, send);
break;
case op_create_blob:
case op_open_blob:
case op_create_blob2:
case op_open_blob2:
port->open_blob(op, &receive->p_blob, send);
break;
case op_batch_segments:
case op_put_segment:
port->put_segment(op, &receive->p_sgmt, send);
break;
case op_get_segment:
port->get_segment(&receive->p_sgmt, send);
break;
case op_seek_blob:
port->seek_blob(&receive->p_seek, send);
break;
case op_cancel_blob:
case op_close_blob:
port->end_blob(op, &receive->p_rlse, send);
break;
case op_prepare2:
port->prepare(&receive->p_prep, send);
break;
case op_release:
port->end_request(&receive->p_rlse, send);
break;
case op_info_blob:
case op_info_database:
case op_info_request:
case op_info_transaction:
case op_service_info:
case op_info_sql:
port->info(op, &receive->p_info, send);
break;
case op_que_events:
port->que_events(&receive->p_event, send);
break;
case op_cancel_events:
cancel_events(port, &receive->p_event, send);
break;
case op_connect_request:
aux_request(port, &receive->p_req, send);
break;
case op_aux_connect:
aux_connect(port, &receive->p_req, send);
break;
case op_ddl:
port->ddl(&receive->p_ddl, send);
break;
case op_get_slice:
port->get_slice(&receive->p_slc, send);
break;
case op_put_slice:
port->put_slice(&receive->p_slc, send);
break;
case op_allocate_statement:
allocate_statement(port, &receive->p_rlse, send);
break;
case op_execute:
case op_execute2:
port->execute_statement(op, &receive->p_sqldata, send);
break;
case op_exec_immediate:
case op_exec_immediate2:
port->execute_immediate(op, &receive->p_sqlst, send);
break;
case op_fetch:
port->fetch(&receive->p_sqldata, send);
break;
case op_free_statement:
port->end_statement(&receive->p_sqlfree, send);
break;
case op_insert:
port->insert(&receive->p_sqldata, send);
break;
case op_prepare_statement:
port->prepare_statement(&receive->p_sqlst, send);
break;
case op_set_cursor:
port->set_cursor(&receive->p_sqlcur, send);
break;
case op_dummy:
send->p_operation = op_dummy;
port->send(send);
break;
default:
sprintf(msg, "SERVER/process_packet: don't understand packet type %d",
receive->p_operation);
gds__log(msg, 0);
port->port_state = state_broken;
break;
}
if (port && port->port_state == state_broken) {
if (!port->port_parent) {
gds__log("SERVER/process_packet: broken port, server exiting", 0);
port->disconnect();
THD_restore_specific();
return FALSE;
}
port->disconnect(send, receive);
port = NULL;
}
if (result)
*result = port;
THD_restore_specific();
} // try
catch (...) {
/* There must be something better to do here. BUT WHAT? */
gds__log("SERVER/process_packet: out of memory", 0);
/* It would be nice to log an error to the user, instead of just terminating them! */
port->send_response(send, 0, 0, trdb->trdb_status_vector);
port->disconnect(send, receive); /* Well, how about this... */
THD_restore_specific();
return FALSE;
}
return TRUE;
}
STATUS port::put_segment(P_OP op, P_SGMT * segment, PACKET* send)
{
/**************************************
*
* p u t _ s e g m e n t
*
**************************************
*
* Functional description
* Write a single blob segment.
*
**************************************/
RBL blob;
USHORT length;
UCHAR *p, *end;
STATUS status_vector[20];
CHECK_HANDLE_MEMBER(blob,
RBL,
type_rbl,
segment->p_sgmt_blob,
isc_bad_segstr_handle);
p = segment->p_sgmt_segment.cstr_address;
length = segment->p_sgmt_segment.cstr_length;
/* Do the signal segment version. If it failed, just pass on the
bad news. */
if (op == op_put_segment) {
THREAD_EXIT;
isc_put_segment(status_vector,
reinterpret_cast <
void **>(GDS_REF(blob->rbl_handle)), length,
reinterpret_cast < char *>(GDS_VAL(p)));
THREAD_ENTER;
return this->send_response(send, 0, 0, status_vector);
}
/* We've got a batch of segments. This is only slightly more complicated */
end = p + length;
while (p < end) {
length = *p++;
length += *p++ << 8;
THREAD_EXIT;
isc_put_segment(status_vector,
reinterpret_cast <
void **>(GDS_REF(blob->rbl_handle)), length,
reinterpret_cast < char *>(GDS_VAL(p)));
THREAD_ENTER;
if (status_vector[1])
return this->send_response(send, 0, 0, status_vector);
p += length;
}
return this->send_response(send, 0, 0, status_vector);
}
STATUS port::put_slice(P_SLC * stuff, PACKET* send)
{
/**************************************
*
* p u t _ s l i c e
*
**************************************
*
* Functional description
* Put an array slice.
*
**************************************/
RDB rdb;
RTR transaction;
STATUS status_vector[20];
CHECK_HANDLE_MEMBER(transaction,
RTR,
type_rtr,
stuff->p_slc_transaction,
isc_bad_trans_handle);
rdb = this->port_context;
THREAD_EXIT;
send->p_resp.p_resp_blob_id = stuff->p_slc_id;
isc_put_slice(status_vector,
reinterpret_cast<void**>(GDS_REF(rdb->rdb_handle)),
reinterpret_cast<void**>(GDS_REF(transaction->rtr_handle)),
(GDS_QUAD *) GDS_REF(send->p_resp.p_resp_blob_id),
stuff->p_slc_sdl.cstr_length,
reinterpret_cast<char*>(GDS_VAL(stuff->p_slc_sdl.cstr_address)),
stuff->p_slc_parameters.cstr_length,
(ISC_LONG *) GDS_VAL(stuff->p_slc_parameters.cstr_address),
stuff->p_slc_slice.lstr_length,
GDS_VAL(stuff->p_slc_slice.lstr_address));
THREAD_ENTER;
return this->send_response(send, 0, 0, status_vector);
}
STATUS port::que_events(P_EVENT * stuff, PACKET* send)
{
/**************************************
*
* q u e _ e v e n t s
*
**************************************
*
* Functional description
*
**************************************/
STATUS status_vector[20];
RDB rdb;
RVNT event;
SLONG id;
rdb = this->port_context;
/* Find unused event block or, if necessary, a new one */
for (event = rdb->rdb_events; event; event = event->rvnt_next) {
if (!event->rvnt_id) {
break;
}
}
if (!event)
{
event = (RVNT) ALLOC(type_rvnt);
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("que_events(server) allocate event %x\n", event);
#endif
event->rvnt_next = rdb->rdb_events;
rdb->rdb_events = event;
}
event->rvnt_ast =
reinterpret_cast<void(*)(void*, USHORT, UCHAR*)>(stuff->p_event_ast);
event->rvnt_arg = (void *) stuff->p_event_arg;
event->rvnt_rid = stuff->p_event_rid;
event->rvnt_rdb = rdb;
THREAD_EXIT;
isc_que_events(status_vector,
reinterpret_cast<void**>(GDS_REF(rdb->rdb_handle)),
GDS_REF(event->rvnt_id),
stuff->p_event_items.cstr_length,
GDS_VAL(stuff->p_event_items.cstr_address),
reinterpret_cast<void (*)()>(server_ast),
GDS_VAL(event));
THREAD_ENTER;
id = event->rvnt_id;
if (status_vector[1]) {
event->rvnt_id = 0;
}
return this->send_response(send, (OBJCT) id, 0, status_vector);
}
STATUS port::receive_after_start( P_DATA* data,
PACKET* send,
STATUS* status_vector)
{
/**************************************
*
* r e c e i v e _ a f t e r _ s t a r t
*
**************************************
*
* Functional description
* Receive a message.
*
**************************************/
RRQ request;
USHORT level, msg_number;
FMT format;
P_RESP *response;
rrq::rrq_repeat* tail;
CHECK_HANDLE_MEMBER(request,
RRQ,
type_rrq,
data->p_data_request,
isc_bad_req_handle);
level = data->p_data_incarnation;
request = REMOTE_find_request(request, level);
/* Figure out the number of the message that we're stalled on. */
if (!get_next_msg_no(request, level, &msg_number)) {
return this->send_response(send, 0, 0, status_vector);
}
send->p_operation = op_response_piggyback;
response = &send->p_resp;
response->p_resp_object = msg_number;
response->p_resp_status_vector = status_vector;
response->p_resp_data.cstr_length = 0;
this->send_partial(send);
/* Fill in packet to fool receive into thinking that it has been
called directly by the client. */
tail = request->rrq_rpt + msg_number;
format = tail->rrq_format;
data->p_data_message_number = msg_number;
if (this->port_flags & PORT_rpc)
{
data->p_data_messages = 1;
}
else
{
data->p_data_messages =
(USHORT) REMOTE_compute_batch_size(this,
(USHORT)
xdr_protocol_overhead
(op_response_piggyback),
op_send,
format);
}
return this->receive_msg(data, send);
}
STATUS port::receive_msg(P_DATA * data, PACKET* send)
{
/**************************************
*
* r e c e i v e _ m s g
*
**************************************
*
* Functional description
* Receive a message.
*
**************************************/
STATUS status_vector[20];
MSG message, next, prior;
RRQ request;
FMT format;
USHORT msg_number, count, count2, level;
#ifdef SCROLLABLE_CURSORS
USHORT direction;
ULONG offset;
#endif
rrq::rrq_repeat* tail;
/* Find the database, request, message number, and the number of
messages the client is willing to cope with. Then locate the
message control block for the request and message type. */
CHECK_HANDLE_MEMBER(request,
RRQ,
type_rrq,
data->p_data_request,
isc_bad_req_handle);
level = data->p_data_incarnation;
request = REMOTE_find_request(request, level);
msg_number = data->p_data_message_number;
count2 = count =
(this->port_flags & PORT_rpc) ? 1 : data->p_data_messages;
tail = request->rrq_rpt + msg_number;
format = tail->rrq_format;
/* Get ready to ship the data out */
send->p_operation = op_send;
send->p_data.p_data_request = data->p_data_request;
send->p_data.p_data_message_number = msg_number;
send->p_data.p_data_incarnation = level;
send->p_data.p_data_messages = 1;
#ifdef SCROLLABLE_CURSORS
/* check the direction and offset for possible redirection; if the user
scrolls in a direction other than we were going or an offset other
than 1, then we need to resynchronize:
if the direction is the same as the lookahead cache, scroll forward
through the cache to see if we find the record; otherwise scroll the
next layer down backward by an amount equal to the number of records
in the cache, plus the amount asked for by the next level up */
if (this->port_protocol < PROTOCOL_SCROLLABLE_CURSORS)
{
direction = blr_forward;
offset = 1;
}
else
{
direction = data->p_data_direction;
offset = data->p_data_offset;
tail->rrq_xdr = scroll_cache(tail, &direction, &offset);
}
#endif
/* Check to see if any messages are already sitting around */
while (TRUE) {
message = tail->rrq_xdr;
/* If we don't have a message cached, get one from the next layer down. */
if (!message->msg_address) {
/* If we have an error queued for delivery, send it now */
if (request->rrq_status_vector[1]) {
STATUS res;
res =
this->send_response(send, 0, 0,
request->rrq_status_vector);
memset(request->rrq_status_vector, 0,
sizeof(request->rrq_status_vector));
return res;
}
THREAD_EXIT;
#ifdef SCROLLABLE_CURSORS
isc_receive2(status_vector,
GDS_REF(request->rrq_handle),
msg_number,
format->fmt_length,
message->msg_buffer, level, direction, offset);
#else
isc_receive(status_vector,
reinterpret_cast<void**>(GDS_REF(request->rrq_handle)),
msg_number,
format->fmt_length,
message->msg_buffer,
level);
#endif
THREAD_ENTER;
if (status_vector[1])
return this->send_response(send, 0, 0, status_vector);
#ifdef SCROLLABLE_CURSORS
/* set the appropriate flags according to the way we just scrolled
the next layer down, and calculate the offset from the beginning
of the result set */
switch (direction) {
case blr_forward:
tail->rrq_flags &= ~RRQ_backward;
tail->rrq_absolute +=
(tail->
rrq_flags & RRQ_absolute_backward) ? -offset : offset;
break;
case blr_backward:
tail->rrq_flags |= RRQ_backward;
tail->rrq_absolute +=
(tail->
rrq_flags & RRQ_absolute_backward) ? offset : -offset;
break;
case blr_bof_forward:
tail->rrq_flags &= ~RRQ_backward;
tail->rrq_flags &= ~RRQ_absolute_backward;
tail->rrq_absolute = offset;
direction = blr_forward;
break;
case blr_eof_backward:
tail->rrq_flags |= RRQ_backward;
tail->rrq_flags |= RRQ_absolute_backward;
tail->rrq_absolute = offset;
direction = blr_backward;
break;
}
message->msg_absolute = tail->rrq_absolute;
/* if we have already scrolled to the location indicated,
then we just want to continue one by one in that direction */
offset = 1;
#endif
message->msg_address = message->msg_buffer;
}
/* If there aren't any buffers remaining, break out of loop */
if (--count <= 0)
break;
/* There's a buffer waiting -- see if the request is ready to send */
next = message->msg_next;
if ((next == message || !next->msg_address) &&
!check_request(request, data->p_data_incarnation, msg_number)) {
/* We've reached the end of the RSE - don't prefetch and flush
everything we've buffered so far */
count2 = 0;
break;
}
if (!this->send_partial(send))
return FALSE;
message->msg_address = NULL;
}
send->p_data.p_data_messages = 0;
this->send(send);
message->msg_address = NULL;
/* Bump up the message pointer to resync with rrq_xdr (rrq_xdr
was incremented by xdr_request in the SEND call). */
tail->rrq_message = message->msg_next;
/* Since we have a little time on our hands while this packet is sent
and processed, get the next batch of records. Start by finding the
next free buffer. */
message = tail->rrq_xdr;
prior = NULL;
while (message->msg_address && message->msg_next != tail->rrq_xdr)
message = message->msg_next;
for (;
count2
&& check_request(request, data->p_data_incarnation, msg_number);
--count2) {
if (message->msg_address) {
if (!prior)
#ifdef SCROLLABLE_CURSORS
prior = message->msg_prior;
#else
for (prior = tail->rrq_xdr; prior->msg_next != message;
prior = prior->msg_next);
#endif
/* allocate a new message block and put it in the cache */
message = (MSG) ALLOCV(type_msg, format->fmt_length);
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("receive_msg(server) allocate message %x\n",
message);
#endif
message->msg_number = prior->msg_number;
message->msg_next = prior->msg_next;
#ifdef SCROLLABLE_CURSORS
message->msg_prior = prior;
#endif
prior->msg_next = message;
prior = message;
}
/* fetch the next record into cache; even for scrollable cursors, we are
just doing a simple lookahead continuing on in the last direction specified,
so there is no reason to do an isc_receive2() */
THREAD_EXIT;
isc_receive(status_vector,
reinterpret_cast<void**>(GDS_REF(request->rrq_handle)),
msg_number,
format->fmt_length,
message->msg_buffer, data->p_data_incarnation);
THREAD_ENTER;
/* Did we have an error? If so, save it for later delivery */
if (status_vector[1]) {
/* If already have an error queued, don't overwrite it */
if (!request->rrq_status_vector[1]) {
memcpy(request->rrq_status_vector, status_vector,
sizeof(request->rrq_status_vector));
}
break;
}
#ifdef SCROLLABLE_CURSORS
/* if we have already scrolled to the location indicated,
then we just want to continue on in that direction */
switch (direction)
{
case blr_forward:
tail->rrq_absolute +=
(tail->rrq_flags & RRQ_absolute_backward) ? -offset : offset;
break;
case blr_backward:
tail->rrq_absolute +=
(tail->rrq_flags & RRQ_absolute_backward) ? offset : -offset;
break;
}
message->msg_absolute = tail->rrq_absolute;
#endif
message->msg_address = message->msg_buffer;
message = message->msg_next;
}
return TRUE;
}
static void release_blob(RBL blob)
{
/**************************************
*
* r e l e a s e _ b l o b
*
**************************************
*
* Functional description
* Release a blob block and friends.
*
**************************************/
RDB rdb = blob->rbl_rdb;
RTR transaction = blob->rbl_rtr;
rdb->rdb_port->port_objects[blob->rbl_id] = NULL;
for (RBL* p = &transaction->rtr_blobs; *p; p = &(*p)->rbl_next) {
if (*p == blob) {
*p = blob->rbl_next;
break;
}
}
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("release_blob(server) free blob %x\n", blob);
#endif
if (blob->rbl_buffer != blob->rbl_data) {
ALLR_free(blob->rbl_buffer);
}
ALLR_release(blob);
}
static void release_event( RVNT event)
{
/**************************************
*
* r e l e a s e _ e v e n t
*
**************************************
*
* Functional description
* Release an event block.
*
**************************************/
RDB rdb;
RVNT *p;
rdb = event->rvnt_rdb;
for (p = &rdb->rdb_events; *p; p = &(*p)->rvnt_next)
if (*p == event) {
*p = event->rvnt_next;
break;
}
ALLR_release(event);
}
static void release_request( RRQ request)
{
/**************************************
*
* r e l e a s e _ r e q u e s t
*
**************************************
*
* Functional description
* Release a request block.
*
**************************************/
RDB rdb;
rdb = request->rrq_rdb;
rdb->rdb_port->port_objects[request->rrq_id] = NULL;
REMOTE_release_request(request);
}
static void release_statement( RSR * statement)
{
/**************************************
*
* r e l e a s e _ s t a t e m e n t
*
**************************************
*
* Functional description
* Release a GDML or SQL statement?
*
**************************************/
if ((*statement)->rsr_select_format)
ALLR_release((*statement)->rsr_select_format);
if ((*statement)->rsr_bind_format)
ALLR_release((*statement)->rsr_bind_format);
REMOTE_release_messages((*statement)->rsr_message);
ALLR_release((*statement));
*statement = NULL;
}
static void release_sql_request( RSR statement)
{
/**************************************
*
* r e l e a s e _ s q l _ r e q u e s t
*
**************************************
*
* Functional description
* Release an SQL request block.
*
**************************************/
RDB rdb;
RSR *p;
rdb = statement->rsr_rdb;
rdb->rdb_port->port_objects[statement->rsr_id] = NULL;
for (p = &rdb->rdb_sql_requests; *p; p = &(*p)->rsr_next)
if (*p == statement) {
*p = statement->rsr_next;
break;
}
release_statement(&statement);
}
static void release_transaction( RTR transaction)
{
/**************************************
*
* r e l e a s e _ t r a n s a c t i o n
*
**************************************
*
* Functional description
* Release a transaction block and friends.
*
**************************************/
RDB rdb;
RTR *p;
rdb = transaction->rtr_rdb;
rdb->rdb_port->port_objects[transaction->rtr_id] = NULL;
while (transaction->rtr_blobs)
release_blob(transaction->rtr_blobs);
for (p = &rdb->rdb_transactions; *p; p = &(*p)->rtr_next)
if (*p == transaction) {
*p = transaction->rtr_next;
break;
}
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("release_transact(server) free transaction %x\n", transaction);
#endif
ALLR_release(transaction);
}
#ifdef SCROLLABLE_CURSORS
static MSG scroll_cache(
rrq::rrq_repeat* tail,
USHORT * direction, ULONG * offset)
{
/**************************************
*
* s c r o l l _ c a c h e
*
**************************************
*
* Functional description
*
* Try to fetch the requested record from cache, if possible. This algorithm depends
* on all scrollable cursors being INSENSITIVE to database changes, so that absolute
* record numbers within the result set will remain constant.
*
* 1. BOF Forward or EOF Backward: Retain the record number of the offset from the
* beginning or end of the result set. If we can figure out the relative offset
* from the absolute, then scroll to it. If it's in cache, great, otherwise dump
* the cache and have the server scroll the correct number of records.
*
* 2. Forward or Backward: Try to scroll the desired offset in cache. If we
* scroll off the end of the cache, dump the cache and ask the server for a
* packetful of records.
*
* This routine differs from the corresponding routine on the client in that
* we are using only a lookahead cache. There is no point in caching records backward,
* in that the client already has them and would not request them from us.
*
**************************************/
MSG message;
/* if we are to continue in the current direction, set direction to
the last direction scrolled; then depending on the direction asked
for, save the last direction asked for by the next layer above */
if (*direction == blr_continue) {
if (tail->rrq_flags & RRQ_last_backward)
*direction = blr_backward;
else
*direction = blr_forward;
}
if (*direction == blr_forward || *direction == blr_bof_forward)
tail->rrq_flags &= ~RRQ_last_backward;
else
tail->rrq_flags |= RRQ_last_backward;
/* set to the first message; if it has no record, this means the cache is
empty and there is no point in trying to find the record here */
message = tail->rrq_xdr;
if (!message->msg_address)
return message;
/* if we are scrolling from BOF and the cache was started from EOF (or vice
versa), the cache is unusable. */
if (
(*direction == blr_bof_forward
&& (tail->rrq_flags & RRQ_absolute_backward))
|| (*direction == blr_eof_backward
&& !(tail->
rrq_flags & RRQ_absolute_backward))) return dump_cache(tail);
/* if we are going to an absolute position, see if we can find that position
in cache, otherwise change to a relative seek from our former position */
if (*direction == blr_bof_forward || *direction == blr_eof_backward) {
/* If offset is before our current position, just dump the cache because
the server cache is purely a lookahead cache--we don't bother to cache
back records because the client will cache those records, making it
unlikely the client would be asking us for a record which is in our cache. */
if (*offset < message->msg_absolute)
return dump_cache(tail);
/* convert the absolute to relative, and prepare to scroll forward to look for the record */
*offset -= message->msg_absolute;
if (*direction == blr_bof_forward)
*direction = blr_forward;
else
*direction = blr_backward;
}
if ((*direction == blr_forward && (tail->rrq_flags & RRQ_backward)) ||
(*direction == blr_backward && !(tail->rrq_flags & RRQ_backward))) {
/* lookahead cache was in opposite direction from the scroll direction,
so increase the scroll amount by the amount we looked ahead, then
dump the cache */
for (message = tail->rrq_xdr; message->msg_address;) {
(*offset)++;
message = message->msg_next;
if (message == tail->rrq_message)
break;
}
return dump_cache(tail);
}
else {
/* lookahead cache is in same direction we want to scroll, so scroll
forward through the cache, decrementing the offset */
for (message = tail->rrq_xdr; message->msg_address;) {
if (*offset == 1)
break;
(*offset)--;
message = message->msg_next;
if (message == tail->rrq_message)
break;
}
}
return message;
}
#endif
STATUS port::seek_blob(P_SEEK * seek, PACKET* send)
{
/**************************************
*
* s e e k _ b l o b
*
**************************************
*
* Functional description
* Execute a blob seek operation.
*
**************************************/
RBL blob;
SSHORT mode;
SLONG offset, result;
STATUS status_vector[20];
CHECK_HANDLE_MEMBER(blob,
RBL,
type_rbl,
seek->p_seek_blob,
isc_bad_segstr_handle);
mode = seek->p_seek_mode;
offset = seek->p_seek_offset;
THREAD_EXIT;
isc_seek_blob(status_vector,
reinterpret_cast<void**>(GDS_REF(blob->rbl_handle)),
mode, offset, GDS_REF(result));
THREAD_ENTER;
send->p_resp.p_resp_blob_id.bid_number = result;
return this->send_response(send, 0, 0, status_vector);
}
STATUS port::send_msg(P_DATA * data, PACKET* send)
{
/**************************************
*
* s e n d _ m s g
*
**************************************
*
* Functional description
* Handle a isc_send operation.
*
**************************************/
STATUS status_vector[20];
RRQ request;
MSG message;
FMT format;
USHORT number;
CHECK_HANDLE_MEMBER(request,
RRQ,
type_rrq,
data->p_data_request,
isc_bad_req_handle);
number = data->p_data_message_number;
request = REMOTE_find_request(request, data->p_data_incarnation);
message = request->rrq_rpt[number].rrq_message;
format = request->rrq_rpt[number].rrq_format;
THREAD_EXIT;
isc_send(status_vector,
reinterpret_cast<void**>(GDS_REF(request->rrq_handle)),
number,
format->fmt_length,
GDS_VAL(message->msg_address), data->p_data_incarnation);
THREAD_ENTER;
message->msg_address = NULL;
return this->send_response(send, 0, 0, status_vector);
}
STATUS port::send_response( PACKET* send,
OBJCT object,
USHORT length,
STATUS* status_vector)
{
/**************************************
*
* s e n d _ r e s p o n s e
*
**************************************
*
* Functional description
* Send a response packet.
*
**************************************/
STATUS *v, new_vector[20], code, exit_code;
USHORT l, sw;
TEXT *p, *q, **sp, buffer[1024];
P_RESP *response;
/* Start by translating the status vector into "generic" form */
v = new_vector;
p = buffer;
exit_code = status_vector[1];
for (sw = TRUE; *status_vector && sw;)
{
switch ((USHORT) * status_vector)
{
case isc_arg_warning:
case gds_arg_gds:
{
USHORT fac = 0, class_ = 0;
/* When talking with older (pre 6.0) clients, do not send
* warnings.
*/
if (*status_vector == isc_arg_warning &&
this->port_protocol < PROTOCOL_VERSION10)
{
sw = FALSE;
continue;
}
*v++ = *status_vector++;
/* The status codes are converted to their offsets so that they
* were compatible with the RDB implementation. This was fine
* when status codes were restricted to a single facility. Now
* that the facility is part of the status code we need to know
* this on the client side, thus when talking with 6.0 and newer
* clients, do not decode the status code, just send it to the
* client. The same check is made in interface.c::check_response
*/
if (this->port_protocol < PROTOCOL_VERSION10)
*v++ = code =
gds__decode(*status_vector++, &fac, &class_);
else
*v++ = code = *status_vector++;
for (;;) {
switch (*status_vector) {
case gds_arg_string:
case gds_arg_number:
*v++ = *status_vector++;
*v++ = *status_vector++;
continue;
case gds_arg_cstring:
*v++ = gds_arg_string;
sp = (TEXT **) v;
*sp++ = p;
v = (STATUS *) sp;
status_vector++;
l = (USHORT) (*status_vector++);
q = (TEXT *) * status_vector++;
if (l)
do
*p++ = *q++;
while (--l);
*p++ = 0;
continue;
}
break;
}
}
continue;
case gds_arg_interpreted:
*v++ = *status_vector++;
*v++ = *status_vector++;
continue;
}
l = (USHORT) gds__interprete(p, &status_vector);
if (l == 0)
break;
*v++ = gds_arg_interpreted;
sp = (TEXT **) v;
*sp++ = p;
v = (STATUS *) sp;
p += l;
sw = FALSE;
}
*v = gds_arg_end;
/* Format and send response. Note: the blob_id and data address fields
of the response packet may contain valid data. Don't trash them. */
send->p_operation = op_response;
response = &send->p_resp;
response->p_resp_object = object;
response->p_resp_status_vector = new_vector;
response->p_resp_data.cstr_length = length;
this->send(send);
return exit_code;
}
static void server_ast( RVNT event, USHORT length, UCHAR * items)
{
/**************************************
*
* s e r v e r _ a s t
*
**************************************
*
* Functional description
* Send an asynchrous event packet to back to client.
*
**************************************/
RDB rdb;
PORT port;
PACKET packet;
P_EVENT *p_event;
THREAD_ENTER;
event->rvnt_id = 0;
rdb = event->rvnt_rdb;
if (!(port = rdb->rdb_port->port_async)) {
THREAD_EXIT;
return;
}
packet.p_operation = op_event;
p_event = &packet.p_event;
p_event->p_event_database = rdb->rdb_id;
p_event->p_event_items.cstr_length = length;
p_event->p_event_items.cstr_address = items;
p_event->p_event_ast = (SLONG) event->rvnt_ast;
p_event->p_event_arg = (SLONG) event->rvnt_arg;
p_event->p_event_rid = event->rvnt_rid;
port->send(&packet);
THREAD_EXIT;
}
STATUS port::service_attach(P_ATCH* attach, PACKET* send)
{
/**************************************
*
* s e r v i c e _ a t t a c h
*
**************************************
*
* Functional description
* Connect to an Interbase service.
*
**************************************/
USHORT service_length, spb_length;
UCHAR *service_name, *spb, new_spb_buffer[512], *new_spb, *p, *end;
int *handle;
STATUS status_vector[20];
RDB rdb;
STR string;
send->p_operation = op_accept;
handle = NULL;
service_name = attach->p_atch_file.cstr_address;
service_length = attach->p_atch_file.cstr_length;
spb = attach->p_atch_dpb.cstr_address;
spb_length = attach->p_atch_dpb.cstr_length;
/* If we have user identification, append it to database parameter block */
new_spb = new_spb_buffer;
if (string = this->port_user_name)
{
if ((spb_length + 3 + string->str_length) > sizeof(new_spb_buffer))
new_spb =
ALLR_alloc((SLONG) (spb_length + 3 + string->str_length));
p = new_spb;
if (spb_length)
for (end = spb + spb_length; spb < end;)
*p++ = *spb++;
else
*p++ = isc_spb_current_version;
*p++ = isc_spb_sys_user_name;
*p++ = (UCHAR) string->str_length;
for (spb = (UCHAR *) string->str_data, end = spb + string->str_length;
spb < end;)
*p++ = *spb++;
spb = new_spb;
spb_length = p - new_spb;
}
/* See if user has specified parameters relevent to the connection,
they will be stuffed in the SPB if so. */
REMOTE_get_timeout_params(this, spb, spb_length);
THREAD_EXIT;
isc_service_attach(status_vector,
service_length,
reinterpret_cast<char*>(service_name),
reinterpret_cast<void**>(&handle),
spb_length,
reinterpret_cast<char*>(spb));
THREAD_ENTER;
if (new_spb != new_spb_buffer)
ALLR_free(new_spb);
if (!status_vector[1]) {
this->port_context = rdb = (RDB) ALLOC(type_rdb);
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("attach_service(server) allocate rdb %x\n", rdb);
#endif
rdb->rdb_port = this;
rdb->rdb_handle = handle;
rdb->rdb_flags |= RDB_service;
}
return this->send_response(send, 0, 0, status_vector);
}
STATUS port::service_end(P_RLSE * release, PACKET* send)
{
/**************************************
*
* s e r v i c e _ e n d
*
**************************************
*
* Functional description
* Close down a service.
*
**************************************/
STATUS status_vector[20];
RDB rdb = this->port_context;
THREAD_EXIT;
isc_service_detach(status_vector,
reinterpret_cast<void**>(GDS_REF(rdb->rdb_handle)));
THREAD_ENTER;
return this->send_response(send, 0, 0, status_vector);
}
STATUS port::service_start(P_INFO * stuff, PACKET* send)
{
/**************************************
*
* s e r v i c e _ s t a r t
*
**************************************
*
* Functional description
* Start a service on the server
*
**************************************/
RDB rdb;
STATUS status_vector[20];
ULONG *reserved = 0; /* reserved for future use */
rdb = this->port_context;
THREAD_EXIT;
isc_service_start(status_vector,
reinterpret_cast<void**>(&rdb->rdb_handle),
reinterpret_cast<long*>(reserved),
stuff->p_info_items.cstr_length,
reinterpret_cast<char*>(stuff->p_info_items.cstr_address));
THREAD_ENTER;
return this->send_response(send, 0, 0, status_vector);
}
STATUS port::set_cursor(P_SQLCUR * sqlcur, PACKET* send)
{
/*****************************************
*
* s e t _ c u r s o r
*
*****************************************
*
* Functional description
* Set a cursor name for a dynamic request.
*
*****************************************/
RSR statement;
STATUS status_vector[20];
CHECK_HANDLE_MEMBER(statement,
RSR,
type_rsr,
sqlcur->p_sqlcur_statement,
isc_bad_req_handle);
THREAD_EXIT;
GDS_DSQL_SET_CURSOR(status_vector,
reinterpret_cast<void**>(&statement->rsr_handle),
reinterpret_cast<char*>(sqlcur->p_sqlcur_cursor_name.cstr_address),
sqlcur->p_sqlcur_type);
THREAD_ENTER;
return this->send_response(send, 0, 0, status_vector);
}
void set_server( PORT port, USHORT flags)
{
/**************************************
*
* s e t _ s e r v e r
*
**************************************
*
* Functional description
* Look up the server for this type
* of port. If one doesn't exist,
* create it.
*
**************************************/
SRVR server;
for (server = servers; server; server = server->srvr_next) {
if (port->port_type == server->srvr_port_type) {
break;
}
}
if (!server) {
server = (SRVR) ALLR_alloc((SLONG) sizeof(struct srvr));
server->srvr_next = servers;
servers = server;
server->srvr_port_type = port->port_type;
server->srvr_parent_port = port;
server->srvr_flags = flags;
}
port->port_server = server;
}
STATUS port::start(P_OP operation, P_DATA * data, PACKET* send)
{
/**************************************
*
* s t a r t
*
**************************************
*
* Functional description
*
**************************************/
STATUS status_vector[20];
RRQ request;
RTR transaction;
CHECK_HANDLE_MEMBER(transaction,
RTR,
type_rtr,
data->p_data_transaction,
isc_bad_trans_handle);
CHECK_HANDLE_MEMBER(request,
RRQ,
type_rrq,
data->p_data_request,
isc_bad_req_handle);
request = REMOTE_find_request(request, data->p_data_incarnation);
REMOTE_reset_request(request, 0);
THREAD_EXIT;
isc_start_request(status_vector,
reinterpret_cast<void**>(GDS_REF(request->rrq_handle)),
reinterpret_cast<void**>(GDS_REF(transaction->rtr_handle)),
data->p_data_incarnation);
THREAD_ENTER;
if (!status_vector[1]) {
request->rrq_rtr = transaction;
if (operation == op_start_and_receive)
return this->receive_after_start(data, send, status_vector);
}
return this->send_response(send, 0, 0, status_vector);
}
STATUS port::start_and_send(P_OP operation,
P_DATA* data,
PACKET* send)
{
/**************************************
*
* s t a r t _ a n d _ s e n d
*
**************************************
*
* Functional description
*
**************************************/
STATUS status_vector[20];
RRQ request;
MSG message;
FMT format;
RTR transaction;
USHORT number;
CHECK_HANDLE_MEMBER(transaction,
RTR,
type_rtr,
data->p_data_transaction,
isc_bad_trans_handle);
CHECK_HANDLE_MEMBER(request,
RRQ,
type_rrq,
data->p_data_request,
isc_bad_req_handle);
request = REMOTE_find_request(request, data->p_data_incarnation);
number = data->p_data_message_number;
message = request->rrq_rpt[number].rrq_message;
format = request->rrq_rpt[number].rrq_format;
REMOTE_reset_request(request, message);
THREAD_EXIT;
isc_start_and_send(status_vector,
reinterpret_cast<void**>(GDS_REF(request->rrq_handle)),
reinterpret_cast<void**>(GDS_REF(transaction->rtr_handle)),
number,
format->fmt_length,
GDS_VAL(message->msg_address),
data->p_data_incarnation);
THREAD_ENTER;
if (!status_vector[1]) {
request->rrq_rtr = transaction;
if (operation == op_start_send_and_receive) {
return this->receive_after_start(data, send, status_vector);
}
}
return this->send_response(send, 0, 0, status_vector);
}
STATUS port::start_transaction(P_OP operation, P_STTR * stuff, PACKET* send)
{
/**************************************
*
* s t a r t _ t r a n s a c t i o n
*
**************************************
*
* Functional description
* Start a transaction.
*
**************************************/
RTR transaction;
OBJCT object;
STATUS status_vector[20];
RDB rdb = this->port_context;
int* handle = NULL;
THREAD_EXIT;
if (operation == op_reconnect)
isc_reconnect_transaction(status_vector,
reinterpret_cast<void**>(GDS_REF(rdb->rdb_handle)),
reinterpret_cast<void**>(GDS_REF(handle)),
stuff->p_sttr_tpb.cstr_length,
reinterpret_cast<char*>(GDS_VAL(stuff->p_sttr_tpb.cstr_address)));
else
isc_start_transaction(status_vector,
reinterpret_cast<void**>(GDS_REF(handle)),
(SSHORT) 1, GDS_REF(rdb->rdb_handle),
stuff->p_sttr_tpb.cstr_length,
GDS_VAL(stuff->p_sttr_tpb.cstr_address));
THREAD_ENTER;
if (status_vector[1])
{
object = 0;
}
else
{
if (transaction = make_transaction(rdb, handle))
{
object = transaction->rtr_id;
if (operation == op_reconnect)
transaction->rtr_flags |= RTR_limbo;
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("start_transaction(server) allocate trans %x\n",
transaction);
#endif
}
else {
object = 0;
THREAD_EXIT;
if (operation != op_reconnect)
isc_rollback_transaction(status_vector,
reinterpret_cast<void**>(GDS_REF(handle)));
#ifdef SUPERSERVER
/* Note that there is an underlying transaction pool
that won't be released until this connection is
detached. There is no isc_disconnect_transaction()
call that gets rid of a transaction reference --
there is only commit and rollback. It's not worth
introducing such a call to all subsystems. At least
release the y-valve handle. */
else {
gds__handle_cleanup(status_vector,
(struct hndl **) GDS_REF(handle));
}
#endif
THREAD_ENTER;
status_vector[0] = isc_arg_gds;
status_vector[1] = isc_too_many_handles;
status_vector[2] = isc_arg_end;
}
}
return this->send_response(send, object, 0, status_vector);
}
static void success( STATUS * status_vector)
{
/**************************************
*
* s u c c e s s
*
**************************************
*
* Functional description
* Set status vector to indicate success.
*
**************************************/
status_vector[0] = gds_arg_gds;
status_vector[1] = SUCCESS;
status_vector[2] = gds_arg_end;
}
static int THREAD_ROUTINE thread(void* flags)
{
/**************************************
*
* t h r e a d
*
**************************************
*
* Functional description
* Execute requests in a happy loop.
*
**************************************/
SERVER_REQ request, next, *req_ptr;
PORT port, parent_port;
SLONG value;
EVENT ptr;
SCHAR *thread;
USHORT inactive_count;
USHORT timedout_count;
#ifdef SUPERSERVER
ISC_enter(); /* Setup floating point exception handler once and for all. */
#endif
#ifdef WIN_NT
if (!((SLONG) flags & SRVR_non_service))
thread = reinterpret_cast<SCHAR*>(CNTL_insert_thread());
#endif
inactive_count = 0;
timedout_count = 0;
THREAD_ENTER;
for (;;)
{
value = ISC_event_clear(thread_event);
if (request = request_que)
{
inactive_count = 0;
timedout_count = 0;
request_que = request->req_next;
while (request)
{
/* Bind a thread to a port. */
if (request->req_port->port_server_flags & SRVR_thread_per_port)
{
port = request->req_port;
request->req_next = free_requests;
free_requests = request;
THREAD_EXIT;
SRVR_main(port, port->port_server_flags);
THREAD_ENTER;
request = 0;
continue;
}
/* Splice request into list of active requests, execute request,
and unsplice */
request->req_next = active_requests;
active_requests = request;
/* Validate port. If it looks ok, process request */
parent_port = request->req_port->port_server->srvr_parent_port;
if (parent_port == request->req_port)
{
process_packet(parent_port, &request->req_send,
&request->req_receive, &port);
}
else
{
for (port = parent_port->port_clients; port;
port = port->port_next)
{
if (port == request->req_port
&& port->port_state != state_disconnected) {
process_packet(port, &request->req_send,
&request->req_receive, &port);
break;
}
}
}
/* Take request out of list of active requests */
for (req_ptr = &active_requests; *req_ptr;
req_ptr = &(*req_ptr)->req_next)
{
if (*req_ptr == request) {
*req_ptr = request->req_next;
break;
}
}
/* If this is a explicit or implicit disconnect, get rid of
any chained requests */
if (!port)
{
while (next = request->req_chain) {
request->req_chain = next->req_chain;
next->req_next = free_requests;
free_requests = next;
}
if (request->req_send.p_operation == op_void &&
request->req_receive.p_operation == op_void) {
gds__free(request);
request = 0;
}
}
else {
port->port_requests_queued--;
#ifdef REMOTE_DEBUG_MEMORY
ib_printf("thread ACTIVE request_queued %d\n",
port->port_requests_queued);
ib_fflush(ib_stdout);
#endif
}
/* Pick up any remaining chained request, and free current request */
if (request) {
next = request->req_chain;
request->req_next = free_requests;
free_requests = request;
request = next;
}
} // while (request)
}
else {
inactive_count++;
/* If the main server thread is not relying on me to take on a new request
and I have been idling for a while, it is time to quit and thereby
release some valuable system resources like memory */
if ((extra_threads > 1)
&& ((inactive_count > 20) || (timedout_count > 2))) {
extra_threads--; /* Count me out */
break;
}
threads_waiting++;
ptr = thread_event;
THREAD_EXIT;
/* Wait for 1 minute (60 seconds) on a new request */
if (ISC_event_wait(1, &ptr, &value, 60 * 1000000, 0, 0))
timedout_count++;
THREAD_ENTER;
--threads_waiting;
}
}
THREAD_EXIT;
#ifdef WIN_NT
if (!((SLONG) flags & SRVR_non_service))
CNTL_remove_thread(thread);
#endif
return 0;
}
STATUS port::transact_request(P_TRRQ * trrq, PACKET* send)
{
/**************************************
*
* t r a n s a c t _ r e q u e s t
*
**************************************
*
* Functional description
*
**************************************/
STATUS status_vector[20];
RDB rdb;
RPR procedure;
RTR transaction;
P_DATA *data;
UCHAR *blr, *in_msg, *out_msg;
USHORT blr_length, in_msg_length, out_msg_length;
CHECK_HANDLE_MEMBER(transaction,
RTR,
type_rtr,
trrq->p_trrq_transaction,
isc_bad_trans_handle);
rdb = this->port_context;
blr = trrq->p_trrq_blr.cstr_address;
blr_length = trrq->p_trrq_blr.cstr_length;
procedure = this->port_rpr;
in_msg =
(procedure->rpr_in_msg) ? procedure->rpr_in_msg->msg_address : NULL;
in_msg_length =
(procedure->rpr_in_format) ? procedure->rpr_in_format->fmt_length : 0;
out_msg =
(procedure->rpr_out_msg) ? procedure->rpr_out_msg->msg_address : NULL;
out_msg_length =
(procedure->rpr_out_format) ? procedure->rpr_out_format->
fmt_length : 0;
THREAD_EXIT;
isc_transact_request(status_vector,
reinterpret_cast<void**>(&rdb->rdb_handle),
reinterpret_cast<void**>(&transaction->rtr_handle),
blr_length,
reinterpret_cast<char*>(blr),
in_msg_length,
reinterpret_cast<char*>(in_msg),
out_msg_length,
reinterpret_cast<char*>(out_msg));
THREAD_ENTER;
if (status_vector[1])
return this->send_response(send, 0, 0, status_vector);
data = &send->p_data;
send->p_operation = op_transact_response;
data->p_data_messages = 1;
this->send(send);
return SUCCESS;
}
static void zap_packet( PACKET* packet, BOOLEAN new_)
{
/**************************************
*
* z a p _ p a c k e t
*
**************************************
*
* Functional description
* Zero out a packet block.
*
**************************************/
if (new_)
memset(packet, 0, sizeof(PACKET));
else
#ifdef DEBUG_XDR_MEMORY
/* Don't trash debug xdr memory allocation table of recycled packets. */
memset(&packet->p_operation, 0,
sizeof(PACKET) - OFFSET(PACKET*, p_operation));
#else
memset(packet, 0, sizeof(PACKET));
#endif
}