8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-26 08:43:03 +01:00
firebird-mirror/src/dsql/dsql.cpp

3226 lines
75 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: Dynamic SQL runtime support
2003-10-05 08:33:56 +02:00
* MODULE: dsql.cpp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: Local processing for External entry points.
*
* 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): ______________________________________.
* 2001.07.06 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
* conditionals, as the engine now fully supports
* readonly databases.
2001-12-24 03:51:06 +01:00
* December 2001 Mike Nordell: Major overhaul to (try to) make it C++
2002-06-29 08:56:51 +02:00
* 2001.6.3 Claudio Valderrama: fixed a bad behaved loop in get_plan_info()
* and get_rsb_item() that caused a crash when plan info was requested.
* 2001.6.9 Claudio Valderrama: Added nod_del_view, nod_current_role and nod_breakleave.
* 2002.10.29 Nickolay Samofatov: Added support for savepoints
2002-10-30 07:40:58 +01:00
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
* 2004.01.16 Vlad Horsun: added support for EXECUTE BLOCK statement
2007-04-13 03:37:44 +02:00
* Adriano dos Santos Fernandes
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2004-04-29 00:00:03 +02:00
#include <stdio.h>
2001-05-23 15:26:42 +02:00
#include <stdlib.h>
#include <string.h>
#include "../dsql/dsql.h"
2008-02-28 14:48:16 +01:00
#include "../dsql/node.h"
2003-11-08 00:27:24 +01:00
#include "../jrd/ibase.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/align.h"
#include "../jrd/intl.h"
2008-02-28 14:48:16 +01:00
#include "../jrd/jrd.h"
#include "../dsql/Parser.h"
2001-05-23 15:26:42 +02:00
#include "../dsql/ddl_proto.h"
#include "../dsql/dsql_proto.h"
#include "../dsql/errd_proto.h"
#include "../dsql/gen_proto.h"
#include "../dsql/hsh_proto.h"
#include "../dsql/make_proto.h"
#include "../dsql/movd_proto.h"
#include "../dsql/parse_proto.h"
#include "../dsql/pass1_proto.h"
#include "../jrd/blb_proto.h"
#include "../jrd/cmp_proto.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/gds_proto.h"
#include "../jrd/inf_proto.h"
2008-02-28 14:48:16 +01:00
#include "../jrd/jrd_proto.h"
#include "../jrd/tra_proto.h"
#include "../common/classes/init.h"
#include "../common/utils_proto.h"
2007-10-06 12:30:53 +02:00
#ifdef SCROLLABLE_CURSORS
#include "../jrd/scroll_cursors.h"
#endif
#include "../common/StatusArg.h"
2007-10-06 12:30:53 +02:00
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
using namespace Jrd;
using namespace Dsql;
using namespace Firebird;
2008-02-28 14:48:16 +01:00
static void close_cursor(thread_db*, dsql_req*);
static USHORT convert(SLONG, UCHAR*);
2008-02-28 14:48:16 +01:00
static void execute_blob(thread_db*, dsql_req*, USHORT, const UCHAR*, USHORT, const UCHAR*,
2001-12-24 03:51:06 +01:00
USHORT, UCHAR*, USHORT, UCHAR*);
2008-02-28 14:48:16 +01:00
static void execute_immediate(thread_db*, Attachment*, jrd_tra**,
USHORT, const TEXT*, USHORT,
USHORT, const UCHAR*, USHORT, USHORT, const UCHAR*,
USHORT, UCHAR*, USHORT, USHORT, UCHAR*);
2008-02-28 14:48:16 +01:00
static void execute_request(thread_db*, dsql_req*, jrd_tra**, USHORT, const UCHAR*,
USHORT, const UCHAR*, USHORT, UCHAR*, USHORT, UCHAR*, bool);
static SSHORT filter_sub_type(dsql_req*, const dsql_nod*);
static bool get_indices(SSHORT*, const SCHAR**, SSHORT*, SCHAR**);
2008-02-28 14:48:16 +01:00
static USHORT get_plan_info(thread_db*, dsql_req*, SSHORT, SCHAR**);
static USHORT get_request_info(thread_db*, dsql_req*, SSHORT, UCHAR*);
2009-01-06 06:53:34 +01:00
static bool get_rsb_item(SSHORT*, const SCHAR**, SSHORT*, SCHAR**, USHORT*, USHORT*);
2008-02-28 14:48:16 +01:00
static dsql_dbb* init(Attachment*);
static void map_in_out(dsql_req*, dsql_msg*, USHORT, const UCHAR*, USHORT, UCHAR*, const UCHAR* = 0);
2004-02-02 12:02:12 +01:00
static USHORT parse_blr(USHORT, const UCHAR*, const USHORT, dsql_par*);
2009-01-29 21:36:29 +01:00
static dsql_req* prepare(thread_db*, dsql_dbb*, jrd_tra*, USHORT, const TEXT*, USHORT, USHORT);
2009-01-06 06:53:34 +01:00
static UCHAR* put_item(UCHAR, const USHORT, const UCHAR*, UCHAR*, const UCHAR* const);
2008-02-28 14:48:16 +01:00
static void release_request(thread_db*, dsql_req*, bool);
static void sql_info(thread_db*, dsql_req*, USHORT, const UCHAR*, USHORT, UCHAR*);
2009-01-29 21:36:29 +01:00
static UCHAR* var_info(dsql_msg*, const UCHAR*, const UCHAR* const, UCHAR*,
const UCHAR* const, USHORT);
2001-05-23 15:26:42 +02:00
#ifdef DSQL_DEBUG
2008-01-16 07:52:43 +01:00
unsigned DSQL_debug = 0;
2003-09-28 02:36:28 +02:00
#endif
2001-05-23 15:26:42 +02:00
2008-02-04 13:45:00 +01:00
namespace
{
2009-01-06 06:53:34 +01:00
const SCHAR db_hdr_info_items[] =
{
2008-02-04 13:45:00 +01:00
isc_info_db_sql_dialect,
isc_info_ods_version,
isc_info_ods_minor_version,
isc_info_base_level,
isc_info_db_read_only,
isc_info_end
};
2009-01-06 06:53:34 +01:00
const SCHAR explain_info[] =
{
2008-02-04 13:45:00 +01:00
isc_info_access_path
};
2009-01-06 06:53:34 +01:00
const SCHAR record_info[] =
{
2008-02-04 13:45:00 +01:00
isc_info_req_update_count, isc_info_req_delete_count,
isc_info_req_select_count, isc_info_req_insert_count
};
2009-01-06 06:53:34 +01:00
const UCHAR sql_records_info[] =
{
2008-02-04 13:45:00 +01:00
isc_info_sql_records
};
} // namespace
2001-05-23 15:26:42 +02:00
#ifdef DSQL_DEBUG
2004-06-14 01:47:02 +02:00
IMPLEMENT_TRACE_ROUTINE(dsql_trace, "DSQL")
#endif
2008-02-28 14:48:16 +01:00
dsql_dbb::~dsql_dbb()
2001-12-24 03:51:06 +01:00
{
2008-02-28 14:48:16 +01:00
HSHD_finish(this);
2001-12-24 03:51:06 +01:00
}
/**
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
DSQL_allocate_statement
2008-12-05 02:20:14 +01:00
@brief Allocate a statement handle.
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
@param tdbb
@param attachment
**/
2009-01-06 06:53:34 +01:00
dsql_req* DSQL_allocate_statement(thread_db* tdbb, Attachment* attachment)
2001-05-23 15:26:42 +02:00
{
2008-02-28 14:48:16 +01:00
SET_TDBB(tdbb);
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
dsql_dbb* const database = init(attachment);
Jrd::ContextPoolHolder context(tdbb, database->createPool());
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// allocate the request block
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
MemoryPool& pool = *tdbb->getDefaultPool();
dsql_req* const request = FB_NEW(pool) dsql_req(pool);
request->req_dbb = database;
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
return request;
2001-05-23 15:26:42 +02:00
}
/**
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
DSQL_execute
2008-12-05 02:20:14 +01:00
@brief Execute a non-SELECT dynamic SQL statement.
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
@param tdbb
@param tra_handle
@param request
@param in_blr_length
@param in_blr
@param in_msg_type
@param in_msg_length
@param in_msg
@param out_blr_length
@param out_blr
@param out_msg_type
@param out_msg_length
@param out_msg
**/
2008-02-28 14:48:16 +01:00
void DSQL_execute(thread_db* tdbb,
jrd_tra** tra_handle,
dsql_req* request,
USHORT in_blr_length, const UCHAR* in_blr,
USHORT in_msg_type, USHORT in_msg_length, const UCHAR* in_msg,
USHORT out_blr_length, UCHAR* out_blr,
USHORT out_msg_type, USHORT out_msg_length, UCHAR* out_msg)
2001-05-23 15:26:42 +02:00
{
2008-02-28 14:48:16 +01:00
SET_TDBB(tdbb);
2008-02-01 21:18:11 +01:00
2008-02-28 14:48:16 +01:00
Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
2008-02-01 21:18:11 +01:00
2008-12-05 02:20:14 +01:00
if (request->req_flags & REQ_orphan)
2008-02-28 14:48:16 +01:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
Arg::Gds(isc_bad_req_handle));
2008-02-28 14:48:16 +01:00
}
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
if ((SSHORT) in_msg_type == -1) {
request->req_type = REQ_EMBED_SELECT;
}
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// Only allow NULL trans_handle if we're starting a transaction
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
if (!*tra_handle && request->req_type != REQ_START_TRANS)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
Arg::Gds(isc_bad_trans_handle));
2008-02-28 14:48:16 +01:00
}
2001-05-23 15:26:42 +02:00
/* If the request is a SELECT or blob statement then this is an open.
Make sure the cursor is not already open. */
2009-01-06 06:53:34 +01:00
switch (request->req_type)
2008-02-28 14:48:16 +01:00
{
2009-01-06 06:53:34 +01:00
case REQ_SELECT:
case REQ_EXEC_BLOCK:
case REQ_SELECT_BLOCK:
case REQ_SELECT_UPD:
case REQ_EMBED_SELECT:
case REQ_GET_SEGMENT:
case REQ_PUT_SEGMENT:
2008-02-28 14:48:16 +01:00
if (request->req_flags & REQ_cursor_open)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) <<
Arg::Gds(isc_dsql_cursor_open_err));
}
2008-02-28 14:48:16 +01:00
}
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// A select with a non zero output length is a singleton select
2008-02-28 14:48:16 +01:00
bool singleton;
if (request->req_type == REQ_SELECT && out_msg_length != 0) {
singleton = true;
}
else {
singleton = false;
}
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
if (request->req_type != REQ_EMBED_SELECT)
{
execute_request(tdbb, request, tra_handle,
2009-01-06 06:53:34 +01:00
in_blr_length, in_blr, in_msg_length, in_msg,
out_blr_length, out_blr, out_msg_length, out_msg,
2008-02-28 14:48:16 +01:00
singleton);
}
else
{
request->req_transaction = *tra_handle;
}
2001-05-23 15:26:42 +02:00
/* If the output message length is zero on a REQ_SELECT then we must
* be doing an OPEN cursor operation.
* If we do have an output message length, then we're doing
2001-05-23 15:26:42 +02:00
* a singleton SELECT. In that event, we don't add the cursor
* to the list of open cursors (it's not really open).
*/
2009-01-06 06:53:34 +01:00
switch (request->req_type)
{
2009-01-06 06:53:34 +01:00
case REQ_SELECT:
if (out_msg_length != 0)
break;
case REQ_SELECT_BLOCK:
case REQ_SELECT_UPD:
case REQ_EMBED_SELECT:
case REQ_GET_SEGMENT:
case REQ_PUT_SEGMENT:
request->req_flags |= REQ_cursor_open;
TRA_link_cursor(request->req_transaction, request);
2008-02-28 14:48:16 +01:00
}
2001-05-23 15:26:42 +02:00
}
2008-02-28 14:48:16 +01:00
/**
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
DSQL_execute_immediate
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
@brief Execute a non-SELECT dynamic SQL statement.
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
@param tdbb
@param attachment
@param tra_handle
@param length
@param string
@param dialect
@param in_blr_length
@param in_blr
@param in_msg_type
@param in_msg_length
@param in_msg
@param out_blr_length
@param out_blr
@param out_msg_type
@param out_msg_length
@param out_msg
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
**/
void DSQL_execute_immediate(thread_db* tdbb,
Attachment* attachment,
jrd_tra** tra_handle,
USHORT length, const TEXT* string, USHORT dialect,
USHORT in_blr_length, const UCHAR* in_blr,
USHORT in_msg_type, USHORT in_msg_length, const UCHAR* in_msg,
USHORT out_blr_length, UCHAR* out_blr,
USHORT out_msg_type, USHORT out_msg_length, UCHAR* out_msg)
2002-04-04 15:53:20 +02:00
{
2008-02-28 14:48:16 +01:00
execute_immediate(tdbb, attachment, tra_handle, length,
2009-01-06 06:53:34 +01:00
string, dialect,
in_blr_length, in_blr, in_msg_type, in_msg_length, in_msg,
out_blr_length, out_blr, out_msg_type, out_msg_length, out_msg);
}
/**
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
DSQL_fetch
2008-12-05 02:20:14 +01:00
@brief Fetch next record from a dynamic SQL cursor
2008-12-05 02:20:14 +01:00
@param user_status
@param req_handle
@param blr_length
@param blr
@param msg_type
@param msg_length
@param dsql_msg
@param direction
@param offset
**/
2008-02-28 14:48:16 +01:00
ISC_STATUS DSQL_fetch(thread_db* tdbb,
dsql_req* request,
USHORT blr_length, const UCHAR* blr,
USHORT msg_type, USHORT msg_length, UCHAR* dsql_msg_buf
2001-05-23 15:26:42 +02:00
#ifdef SCROLLABLE_CURSORS
2008-02-28 14:48:16 +01:00
, USHORT direction, SLONG offset
2001-05-23 15:26:42 +02:00
#endif
2008-02-28 14:48:16 +01:00
)
2001-05-23 15:26:42 +02:00
{
2008-02-28 14:48:16 +01:00
SET_TDBB(tdbb);
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// if the cursor isn't open, we've got a problem
if (!(request->req_flags & REQ_cursor_open))
2008-10-31 01:02:49 +01:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) <<
Arg::Gds(isc_dsql_cursor_err) <<
Arg::Gds(isc_dsql_cursor_not_open));
2008-10-31 01:02:49 +01:00
}
2001-05-23 15:26:42 +02:00
#ifdef SCROLLABLE_CURSORS
/* check whether we need to send an asynchronous scrolling message
to the engine; the engine will automatically advance one record
in the same direction as before, so optimize out messages of that
2001-05-23 15:26:42 +02:00
type */
2009-01-06 06:53:34 +01:00
if (request->req_type == REQ_SELECT && request->req_dbb->dbb_base_level >= 5)
2008-02-28 14:48:16 +01:00
{
switch (direction)
{
2008-02-28 14:48:16 +01:00
case isc_fetch_next:
if (!(request->req_flags & REQ_backwards))
offset = 0;
else {
direction = blr_forward;
2001-05-23 15:26:42 +02:00
offset = 1;
request->req_flags &= ~REQ_backwards;
2008-02-28 14:48:16 +01:00
}
break;
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
case isc_fetch_prior:
if (request->req_flags & REQ_backwards)
offset = 0;
else {
direction = blr_backward;
2001-05-23 15:26:42 +02:00
offset = 1;
request->req_flags |= REQ_backwards;
2008-02-28 14:48:16 +01:00
}
break;
2008-02-28 14:48:16 +01:00
case isc_fetch_first:
direction = blr_bof_forward;
offset = 1;
request->req_flags &= ~REQ_backwards;
break;
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
case isc_fetch_last:
direction = blr_eof_backward;
offset = 1;
request->req_flags |= REQ_backwards;
break;
2008-02-28 14:48:16 +01:00
case isc_fetch_absolute:
direction = blr_bof_forward;
request->req_flags &= ~REQ_backwards;
break;
case isc_fetch_relative:
if (offset < 0) {
direction = blr_backward;
offset = -offset;
request->req_flags |= REQ_backwards;
}
else {
direction = blr_forward;
request->req_flags &= ~REQ_backwards;
}
2008-02-28 14:48:16 +01:00
break;
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
default:
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-804) <<
Arg::Gds(isc_dsql_sqlda_err));
2008-02-28 14:48:16 +01:00
}
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
if (offset)
{
DSC desc;
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
dsql_msg* message = (dsql_msg*) request->req_async;
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
desc.dsc_dtype = dtype_short;
desc.dsc_scale = 0;
desc.dsc_length = sizeof(USHORT);
desc.dsc_flags = 0;
desc.dsc_address = (UCHAR*) & direction;
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
dsql_par* offset_parameter = message->msg_parameters;
dsql_par* parameter = offset_parameter->par_next;
MOVD_move(&desc, &parameter->par_desc);
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
desc.dsc_dtype = dtype_long;
desc.dsc_scale = 0;
desc.dsc_length = sizeof(SLONG);
desc.dsc_flags = 0;
desc.dsc_address = (UCHAR*) & offset;
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
MOVD_move(&desc, &offset_parameter->par_desc);
2008-02-01 21:18:11 +01:00
2008-02-28 14:48:16 +01:00
DsqlCheckout dcoHolder(request->req_dbb);
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
if (isc_receive2(tdbb->tdbb_status_vector, &request->req_request,
message->msg_number, message->msg_length,
message->msg_buffer, 0, direction, offset))
{
Firebird::status_exception::raise(tdbb->tdbb_status_vector);
}
2001-05-23 15:26:42 +02:00
}
2008-02-28 14:48:16 +01:00
}
2001-05-23 15:26:42 +02:00
#endif
2008-02-28 14:48:16 +01:00
dsql_msg* message = (dsql_msg*) request->req_receive;
2001-05-23 15:26:42 +02:00
/* Insure that the blr for the message is parsed, regardless of
whether anything is found by the call to receive. */
2008-02-28 14:48:16 +01:00
if (blr_length) {
parse_blr(blr_length, blr, msg_length, message->msg_parameters);
}
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
if (request->req_type == REQ_GET_SEGMENT)
{
// For get segment, use the user buffer and indicator directly.
2008-02-01 21:18:11 +01:00
2008-02-28 14:48:16 +01:00
dsql_par* parameter = request->req_blob->blb_segment;
dsql_par* null = parameter->par_null;
2009-01-06 06:53:34 +01:00
USHORT* ret_length = (USHORT *) (dsql_msg_buf + (IPTR) null->par_user_desc.dsc_address);
2008-02-28 14:48:16 +01:00
UCHAR* buffer = dsql_msg_buf + (IPTR) parameter->par_user_desc.dsc_address;
2008-02-01 21:18:11 +01:00
*ret_length = BLB_get_segment(tdbb, request->req_blob->blb_blob,
buffer, parameter->par_user_desc.dsc_length);
2001-05-23 15:26:42 +02:00
if (request->req_blob->blb_blob->blb_flags & BLB_eof)
return 100;
2008-06-05 13:02:42 +02:00
if (request->req_blob->blb_blob->blb_fragment_size)
return 101;
2008-06-05 13:02:42 +02:00
return 0;
2008-02-28 14:48:16 +01:00
}
2001-05-23 15:26:42 +02:00
JRD_receive(tdbb, request->req_request, message->msg_number, message->msg_length,
message->msg_buffer, 0);
2008-02-28 14:48:16 +01:00
const dsql_par* const eof = request->req_eof;
if (eof)
{
2008-02-28 14:48:16 +01:00
if (!*((USHORT *) eof->par_desc.dsc_address)) {
return 100;
}
}
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
map_in_out(NULL, message, 0, blr, msg_length, dsql_msg_buf);
return FB_SUCCESS;
2001-05-23 15:26:42 +02:00
}
/**
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
DSQL_free_statement
2008-12-05 02:20:14 +01:00
@brief Release request for a dsql statement
2008-12-05 02:20:14 +01:00
@param user_status
@param req_handle
@param option
**/
2009-01-06 06:53:34 +01:00
void DSQL_free_statement(thread_db* tdbb, dsql_req* request, USHORT option)
2001-05-23 15:26:42 +02:00
{
2008-02-28 14:48:16 +01:00
SET_TDBB(tdbb);
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
if (option & DSQL_drop) {
// Release everything associated with the request
release_request(tdbb, request, true);
}
2008-02-28 14:48:16 +01:00
else if (option & DSQL_unprepare) {
// Release everything but the request itself
release_request(tdbb, request, false);
}
else if (option & DSQL_close) {
// Just close the cursor associated with the request
if (!(request->req_flags & REQ_cursor_open))
{
2008-08-16 17:42:38 +02:00
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-501) <<
Arg::Gds(isc_dsql_cursor_close_err));
2008-02-28 14:48:16 +01:00
}
close_cursor(tdbb, request);
2001-05-23 15:26:42 +02:00
}
}
/**
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
DSQL_insert
2008-12-05 02:20:14 +01:00
@brief Insert next record into a dynamic SQL cursor
2008-12-05 02:20:14 +01:00
@param user_status
@param req_handle
@param blr_length
@param blr
@param msg_type
@param msg_length
@param dsql_msg
**/
2008-02-28 14:48:16 +01:00
void DSQL_insert(thread_db* tdbb,
dsql_req* request,
USHORT blr_length, const UCHAR* blr,
2008-03-02 09:40:09 +01:00
USHORT msg_type, USHORT msg_length, const UCHAR* dsql_msg_buf)
2001-05-23 15:26:42 +02:00
{
2008-02-28 14:48:16 +01:00
SET_TDBB(tdbb);
2008-02-01 21:18:11 +01:00
2008-02-28 14:48:16 +01:00
Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
2008-02-01 21:18:11 +01:00
2008-12-05 02:20:14 +01:00
if (request->req_flags & REQ_orphan)
2008-02-28 14:48:16 +01:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
Arg::Gds(isc_bad_req_handle));
2008-02-28 14:48:16 +01:00
}
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// if the cursor isn't open, we've got a problem
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
if (request->req_type == REQ_PUT_SEGMENT)
{
if (!(request->req_flags & REQ_cursor_open))
2008-02-01 21:18:11 +01:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) <<
2008-08-16 17:42:38 +02:00
Arg::Gds(isc_dsql_cursor_err) <<
Arg::Gds(isc_dsql_cursor_not_open));
2008-02-01 21:18:11 +01:00
}
2008-02-28 14:48:16 +01:00
}
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
dsql_msg* message = (dsql_msg*) request->req_receive;
2001-05-23 15:26:42 +02:00
/* Insure that the blr for the message is parsed, regardless of
whether anything is found by the call to receive. */
2008-02-28 14:48:16 +01:00
if (blr_length)
parse_blr(blr_length, blr, msg_length, message->msg_parameters);
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
if (request->req_type == REQ_PUT_SEGMENT) {
2008-12-05 02:20:14 +01:00
// For put segment, use the user buffer and indicator directly.
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
dsql_par* parameter = request->req_blob->blb_segment;
2008-03-01 16:08:11 +01:00
const UCHAR* buffer = dsql_msg_buf + (IPTR) parameter->par_user_desc.dsc_address;
2008-02-01 21:18:11 +01:00
BLB_put_segment(tdbb, request->req_blob->blb_blob, buffer,
parameter->par_user_desc.dsc_length);
}
2001-05-23 15:26:42 +02:00
}
/**
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
DSQL_prepare
2008-12-05 02:20:14 +01:00
@brief Prepare a statement for execution.
2008-12-05 02:20:14 +01:00
@param user_status
@param trans_handle
@param req_handle
@param length
@param string
@param dialect
@param item_length
@param items
@param buffer_length
@param buffer
**/
2008-02-28 14:48:16 +01:00
void DSQL_prepare(thread_db* tdbb,
jrd_tra* transaction,
dsql_req** req_handle,
USHORT length, const TEXT* string, USHORT dialect,
USHORT item_length, const UCHAR* items,
USHORT buffer_length, UCHAR* buffer)
2001-05-23 15:26:42 +02:00
{
2008-02-28 14:48:16 +01:00
SET_TDBB(tdbb);
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
dsql_req* const old_request = *req_handle;
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
if (!old_request) {
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
Arg::Gds(isc_bad_req_handle));
2008-02-28 14:48:16 +01:00
}
2002-06-29 08:56:51 +02:00
2008-02-28 14:48:16 +01:00
dsql_dbb* database = old_request->req_dbb;
if (!database) {
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
Arg::Gds(isc_bad_req_handle));
2008-02-28 14:48:16 +01:00
}
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// check to see if old request has an open cursor
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
if (old_request && (old_request->req_flags & REQ_cursor_open)) {
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-519) <<
Arg::Gds(isc_dsql_open_cursor_request));
2008-02-28 14:48:16 +01:00
}
2001-05-23 15:26:42 +02:00
dsql_req* request = NULL;
2001-05-23 15:26:42 +02:00
if (!string) {
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
// Unexpected end of command
// CVC: Nothing will be line 1, column 1 for the user.
Arg::Gds(isc_command_end_err2) << Arg::Num(1) << Arg::Num(1));
}
2001-05-23 15:26:42 +02:00
if (!length) {
length = strlen(string);
}
try {
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// Figure out which parser version to use
2003-09-28 02:36:28 +02:00
/* Since the API to dsql8_prepare is public and can not be changed, there needs to
2001-05-23 15:26:42 +02:00
* 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 destinations. The information
2001-05-23 15:26:42 +02:00
* 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
*
* If the parser version is not part of the dialect, then assume that the
2001-05-23 15:26:42 +02:00
* connection being made is a local classic connection.
*/
2008-02-28 14:48:16 +01:00
USHORT parser_version;
if ((dialect / 10) == 0)
parser_version = 2;
else {
parser_version = dialect % 10;
dialect /= 10;
}
2001-05-23 15:26:42 +02:00
2008-05-25 03:39:16 +02:00
// Allocate a new request block and then prepare the request. We want to
// keep the old request around, as is, until we know that we are able
// to prepare the new one.
// It would be really *nice* to know *why* we want to
// keep the old request around -- 1994-October-27 David Schnepper
// Because that's the client's allocated statement handle and we
// don't want to trash the context in it -- 2001-Oct-27 Ann Harrison
2009-01-29 21:36:29 +01:00
request = prepare(tdbb, database, transaction, length, string, dialect, parser_version);
2008-03-01 16:08:11 +01:00
// Can not prepare a CREATE DATABASE/SCHEMA statement
2008-02-28 14:48:16 +01:00
if (request->req_type == REQ_CREATE_DB)
2001-12-24 03:51:06 +01:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-530) <<
Arg::Gds(isc_dsql_crdb_prepare_err));
2004-12-26 16:32:49 +01:00
}
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
request->req_flags |= REQ_prepared;
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// Now that we know that the new request exists, zap the old one.
2001-12-24 03:51:06 +01:00
2008-02-28 14:48:16 +01:00
{
Jrd::ContextPoolHolder context(tdbb, &old_request->req_pool);
2008-02-28 14:48:16 +01:00
release_request(tdbb, old_request, true);
}
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
*req_handle = request;
2001-05-23 15:26:42 +02:00
Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
2008-02-28 14:48:16 +01:00
sql_info(tdbb, request, item_length, items, buffer_length, buffer);
}
catch (const Firebird::Exception&)
{
if (request)
{
Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
release_request(tdbb, request, true);
}
2008-02-28 14:48:16 +01:00
throw;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
}
/**
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
DSQL_set_cursor_name
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
@brief Set a cursor name for a dynamic request
2008-12-05 02:20:14 +01:00
@param user_status
@param req_handle
2008-02-28 14:48:16 +01:00
@param input_cursor
@param type
**/
2008-02-28 14:48:16 +01:00
void DSQL_set_cursor(thread_db* tdbb,
dsql_req* request,
const TEXT* input_cursor,
USHORT type)
{
SET_TDBB(tdbb);
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
2008-02-28 14:48:16 +01:00
const size_t MAX_CURSOR_LENGTH = 132 - 1;
Firebird::string cursor = input_cursor;
2008-02-28 14:48:16 +01:00
if (cursor[0] == '\"')
{
// Quoted cursor names eh? Strip'em.
// Note that "" will be replaced with ".
// The code is very strange, because it doesn't check for "" really
// and thus deletes one isolated " in the middle of the cursor.
2009-01-06 06:53:34 +01:00
for (Firebird::string::iterator i = cursor.begin(); i < cursor.end(); ++i)
2008-02-28 14:48:16 +01:00
{
if (*i == '\"') {
cursor.erase(i);
}
2001-05-23 15:26:42 +02:00
}
2008-02-28 14:48:16 +01:00
}
else // not quoted name
{
2009-01-06 06:53:34 +01:00
const Firebird::string::size_type i = cursor.find(' ');
2008-02-28 14:48:16 +01:00
if (i != Firebird::string::npos)
{
cursor.resize(i);
2001-05-23 15:26:42 +02:00
}
2008-02-28 14:48:16 +01:00
cursor.upper();
}
USHORT length = (USHORT) fb_utils::name_length(cursor.c_str());
if (length == 0) {
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) <<
Arg::Gds(isc_dsql_decl_err) <<
Arg::Gds(isc_dsql_cursor_invalid));
2008-02-28 14:48:16 +01:00
}
if (length > MAX_CURSOR_LENGTH) {
length = MAX_CURSOR_LENGTH;
2001-05-23 15:26:42 +02:00
}
2008-02-28 14:48:16 +01:00
cursor.resize(length);
2008-12-05 02:20:14 +01:00
// If there already is a different cursor by the same name, bitch
2008-02-28 14:48:16 +01:00
2009-01-06 06:53:34 +01:00
const dsql_sym* symbol = HSHD_lookup(request->req_dbb, cursor.c_str(), length, SYM_cursor, 0);
2008-02-28 14:48:16 +01:00
if (symbol)
{
if (request->req_cursor == symbol)
return;
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) <<
Arg::Gds(isc_dsql_decl_err) <<
Arg::Gds(isc_dsql_cursor_redefined) << Arg::Str(symbol->sym_string));
}
2001-05-23 15:26:42 +02:00
2008-03-01 16:08:11 +01:00
// If there already is a cursor and its name isn't the same, ditto.
// We already know there is no cursor by this name in the hash table
2008-02-28 14:48:16 +01:00
if (!request->req_cursor) {
2009-01-06 06:53:34 +01:00
request->req_cursor = MAKE_symbol(request->req_dbb, cursor.c_str(), length, SYM_cursor, request);
2008-02-28 14:48:16 +01:00
}
else {
fb_assert(request->req_cursor != symbol);
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-502) <<
Arg::Gds(isc_dsql_decl_err) <<
Arg::Gds(isc_dsql_cursor_redefined) << Arg::Str(request->req_cursor->sym_string));
2001-05-23 15:26:42 +02:00
}
}
/**
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
DSQL_sql_info
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
@brief Provide information on dsql statement
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
@param user_status
@param req_handle
@param item_length
@param items
@param info_length
@param info
**/
2008-02-28 14:48:16 +01:00
void DSQL_sql_info(thread_db* tdbb,
dsql_req* request,
USHORT item_length, const UCHAR* items,
USHORT info_length, UCHAR* info)
2001-05-23 15:26:42 +02:00
{
2008-02-28 14:48:16 +01:00
SET_TDBB(tdbb);
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
sql_info(tdbb, request, item_length, items, info_length, info);
2001-05-23 15:26:42 +02:00
}
/**
2008-12-05 02:20:14 +01:00
close_cursor
2008-12-05 02:20:14 +01:00
@brief Close an open cursor.
2008-12-05 02:20:14 +01:00
@param request
@param tdbb
**/
static void close_cursor(thread_db* tdbb, dsql_req* request)
2001-05-23 15:26:42 +02:00
{
SET_TDBB(tdbb);
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
if (request->req_request)
{
ThreadStatusGuard status_vector(tdbb);
try
{
2009-01-06 06:53:34 +01:00
if (request->req_type == REQ_GET_SEGMENT || request->req_type == REQ_PUT_SEGMENT)
{
BLB_close(tdbb, request->req_blob->blb_blob);
request->req_blob->blb_blob = NULL;
}
else
JRD_unwind_request(tdbb, request->req_request, 0);
2004-09-09 10:56:33 +02:00
}
catch (Firebird::Exception&)
2008-02-28 14:48:16 +01:00
{
2001-05-23 15:26:42 +02:00
}
2003-09-13 14:16:48 +02:00
}
2001-05-23 15:26:42 +02:00
request->req_flags &= ~REQ_cursor_open;
2008-02-28 14:48:16 +01:00
TRA_unlink_cursor(request->req_transaction, request);
2001-05-23 15:26:42 +02:00
}
/**
2008-12-05 02:20:14 +01:00
convert
2008-12-05 02:20:14 +01:00
@brief Convert a number to VAX form -- least significant bytes first.
Return the length.
2008-12-05 02:20:14 +01:00
@param number
@param buffer
**/
// CVC: This routine should disappear in favor of a centralized function.
static USHORT convert( SLONG number, UCHAR* buffer)
2001-05-23 15:26:42 +02:00
{
const UCHAR* p;
2001-05-23 15:26:42 +02:00
#ifndef WORDS_BIGENDIAN
2008-06-06 04:25:35 +02:00
p = (UCHAR*) &number;
2001-05-23 15:26:42 +02:00
*buffer++ = *p++;
*buffer++ = *p++;
*buffer++ = *p++;
*buffer++ = *p++;
#else
2008-06-06 04:25:35 +02:00
p = (UCHAR*) (&number + 1);
2001-05-23 15:26:42 +02:00
*buffer++ = *--p;
*buffer++ = *--p;
*buffer++ = *--p;
*buffer++ = *--p;
#endif
return 4;
}
/**
2008-12-05 02:20:14 +01:00
execute_blob
2008-12-05 02:20:14 +01:00
@brief Open or create a blob.
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
@param tdbb
@param request
@param in_blr_length
@param in_blr
@param in_msg_length
@param in_msg
@param out_blr_length
@param out_blr
@param out_msg_length
@param out_msg
**/
2008-02-28 14:48:16 +01:00
static void execute_blob(thread_db* tdbb,
dsql_req* request,
USHORT in_blr_length,
const UCHAR* in_blr,
USHORT in_msg_length,
const UCHAR* in_msg,
USHORT out_blr_length,
UCHAR* out_blr,
USHORT out_msg_length,
UCHAR* out_msg)
2001-05-23 15:26:42 +02:00
{
UCHAR bpb[24];
2001-05-23 15:26:42 +02:00
2003-11-10 10:16:38 +01:00
dsql_blb* blob = request->req_blob;
2009-01-06 06:53:34 +01:00
map_in_out(request, blob->blb_open_in_msg, in_blr_length, in_blr, in_msg_length, NULL, in_msg);
2001-05-23 15:26:42 +02:00
UCHAR* p = bpb;
2003-11-08 00:27:24 +01:00
*p++ = isc_bpb_version1;
SSHORT filter = filter_sub_type(request, blob->blb_to);
if (filter) {
2003-11-08 00:27:24 +01:00
*p++ = isc_bpb_target_type;
2001-05-23 15:26:42 +02:00
*p++ = 2;
*p++ = static_cast<UCHAR>(filter);
*p++ = filter >> 8;
}
filter = filter_sub_type(request, blob->blb_from);
if (filter) {
2003-11-08 00:27:24 +01:00
*p++ = isc_bpb_source_type;
2001-05-23 15:26:42 +02:00
*p++ = 2;
*p++ = static_cast<UCHAR>(filter);
*p++ = filter >> 8;
}
USHORT bpb_length = p - bpb;
2001-12-24 03:51:06 +01:00
if (bpb_length == 1) {
2001-05-23 15:26:42 +02:00
bpb_length = 0;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
dsql_par* parameter = blob->blb_blob_id;
const dsql_par* null = parameter->par_null;
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
if (request->req_type == REQ_GET_SEGMENT)
{
2008-02-28 14:48:16 +01:00
bid* blob_id = (bid*) parameter->par_desc.dsc_address;
2001-12-24 03:51:06 +01:00
if (null && *((SSHORT *) null->par_desc.dsc_address) < 0) {
2008-02-28 14:48:16 +01:00
memset(blob_id, 0, sizeof(bid));
2001-12-24 03:51:06 +01:00
}
2008-02-01 21:18:11 +01:00
2009-01-06 06:53:34 +01:00
request->req_blob->blb_blob =
BLB_open2(tdbb, request->req_transaction, blob_id, bpb_length, bpb, true);
2001-05-23 15:26:42 +02:00
}
2001-12-24 03:51:06 +01:00
else
{
request->req_request = NULL;
2008-02-28 14:48:16 +01:00
bid* blob_id = (bid*) parameter->par_desc.dsc_address;
memset(blob_id, 0, sizeof(bid));
2008-02-01 21:18:11 +01:00
2009-01-06 06:53:34 +01:00
request->req_blob->blb_blob =
BLB_create2(tdbb, request->req_transaction, blob_id, bpb_length, bpb);
2008-02-01 21:18:11 +01:00
2009-01-06 06:53:34 +01:00
map_in_out(NULL, blob->blb_open_out_msg, out_blr_length, out_blr, out_msg_length, out_msg);
2008-02-28 14:48:16 +01:00
}
}
/**
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
execute_immediate
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
@brief Common part of prepare and execute a statement.
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
@param tdbb
@param attachment
@param tra_handle
@param length
@param string
@param dialect
@param in_blr_length
@param in_blr
@param in_msg_type
@param in_msg_length
@param in_msg
@param out_blr_length
@param out_blr
@param out_msg_type
@param out_msg_length
@param out_msg
@param possible_requests
**/
static void execute_immediate(thread_db* tdbb,
Attachment* attachment,
jrd_tra** tra_handle,
USHORT length, const TEXT* string, USHORT dialect,
USHORT in_blr_length, const UCHAR* in_blr,
USHORT in_msg_type, USHORT in_msg_length, const UCHAR* in_msg,
USHORT out_blr_length, UCHAR* out_blr,
USHORT out_msg_type, USHORT out_msg_length, UCHAR* out_msg)
2008-02-28 14:48:16 +01:00
{
SET_TDBB(tdbb);
if (!string) {
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
// Unexpected end of command
// CVC: Nothing will be line 1, column 1 for the user.
Arg::Gds(isc_command_end_err2) << Arg::Num(1) << Arg::Num(1));
}
if (!length) {
length = strlen(string);
}
2008-02-28 14:48:16 +01:00
dsql_dbb* const database = init(attachment);
dsql_req* request = NULL;
2008-02-28 14:48:16 +01:00
try {
2008-12-05 02:20:14 +01:00
// Figure out which parser version to use
2008-02-28 14:48:16 +01:00
/* Since the API to dsql8_execute_immediate 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 destinations. 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
*
* If the parser version is not part of the dialect, then assume that the
* connection being made is a local classic connection.
*/
USHORT parser_version;
if ((dialect / 10) == 0)
parser_version = 2;
else {
parser_version = dialect % 10;
dialect /= 10;
}
2009-01-29 21:36:29 +01:00
request = prepare(tdbb, database, *tra_handle, length, string, dialect, parser_version);
2008-02-28 14:48:16 +01:00
Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
2008-02-28 14:48:16 +01:00
2009-01-06 06:53:34 +01:00
execute_request(tdbb, request, tra_handle,
in_blr_length, in_blr, in_msg_length, in_msg,
out_blr_length, out_blr, out_msg_length, out_msg,
false);
2008-02-28 14:48:16 +01:00
release_request(tdbb, request, true);
}
catch (const Firebird::Exception&)
{
if (request)
{
Jrd::ContextPoolHolder context(tdbb, &request->req_pool);
release_request(tdbb, request, true);
}
2008-02-28 14:48:16 +01:00
throw;
2001-05-23 15:26:42 +02:00
}
}
/**
2008-12-05 02:20:14 +01:00
execute_request
2008-12-05 02:20:14 +01:00
@brief Execute a dynamic SQL statement.
2008-12-05 02:20:14 +01:00
2008-02-28 14:48:16 +01:00
@param tdbb
@param request
@param trans_handle
@param in_blr_length
@param in_blr
@param in_msg_length
@param in_msg
@param out_blr_length
@param out_blr
@param out_msg_length
@param out_msg
@param singleton
**/
2008-02-28 14:48:16 +01:00
static void execute_request(thread_db* tdbb,
dsql_req* request,
jrd_tra** tra_handle,
USHORT in_blr_length, const UCHAR* in_blr,
USHORT in_msg_length, const UCHAR* in_msg,
USHORT out_blr_length, UCHAR* out_blr,
USHORT out_msg_length, UCHAR* out_msg,
bool singleton)
2001-05-23 15:26:42 +02:00
{
2008-02-28 14:48:16 +01:00
request->req_transaction = *tra_handle;
ISC_STATUS return_status = FB_SUCCESS;
2001-05-23 15:26:42 +02:00
2008-02-01 21:18:11 +01:00
switch (request->req_type)
{
2001-05-23 15:26:42 +02:00
case REQ_START_TRANS:
2009-01-06 06:53:34 +01:00
JRD_start_transaction(tdbb, &request->req_transaction, 1, &request->req_dbb->dbb_attachment,
request->req_blr_data.getCount(), request->req_blr_data.begin());
*tra_handle = request->req_transaction;
return;
2001-05-23 15:26:42 +02:00
case REQ_COMMIT:
JRD_commit_transaction(tdbb, &request->req_transaction);
*tra_handle = NULL;
return;
2001-05-23 15:26:42 +02:00
case REQ_COMMIT_RETAIN:
JRD_commit_retaining(tdbb, &request->req_transaction);
return;
2001-05-23 15:26:42 +02:00
case REQ_ROLLBACK:
JRD_rollback_transaction(tdbb, &request->req_transaction);
*tra_handle = NULL;
return;
2001-05-23 15:26:42 +02:00
case REQ_ROLLBACK_RETAIN:
JRD_rollback_retaining(tdbb, &request->req_transaction);
return;
2008-02-28 14:48:16 +01:00
case REQ_CREATE_DB:
2001-05-23 15:26:42 +02:00
case REQ_DDL:
DDL_execute(request);
2008-02-28 14:48:16 +01:00
return;
2001-05-23 15:26:42 +02:00
case REQ_GET_SEGMENT:
2008-02-28 14:48:16 +01:00
execute_blob(tdbb, request,
in_blr_length, in_blr, in_msg_length, in_msg,
out_blr_length, out_blr, out_msg_length, out_msg);
return;
2001-05-23 15:26:42 +02:00
case REQ_PUT_SEGMENT:
2008-02-28 14:48:16 +01:00
execute_blob(tdbb, request,
in_blr_length, in_blr, in_msg_length, in_msg,
out_blr_length, out_blr, out_msg_length, out_msg);
return;
2001-05-23 15:26:42 +02:00
case REQ_SELECT:
case REQ_SELECT_UPD:
case REQ_EMBED_SELECT:
2001-05-23 15:26:42 +02:00
case REQ_INSERT:
case REQ_UPDATE:
case REQ_UPDATE_CURSOR:
case REQ_DELETE:
2001-05-23 15:26:42 +02:00
case REQ_DELETE_CURSOR:
case REQ_EXEC_PROCEDURE:
2001-05-23 15:26:42 +02:00
case REQ_SET_GENERATOR:
case REQ_SAVEPOINT:
case REQ_EXEC_BLOCK:
case REQ_SELECT_BLOCK:
2001-05-23 15:26:42 +02:00
break;
default:
2008-12-05 02:20:14 +01:00
// Catch invalid request types
fb_assert(false);
2001-05-23 15:26:42 +02:00
}
2008-12-05 02:20:14 +01:00
// If there is no data required, just start the request
2001-05-23 15:26:42 +02:00
dsql_msg* message = request->req_send;
2001-05-24 16:54:26 +02:00
if (!message)
JRD_start(tdbb, request->req_request, request->req_transaction, 0);
2001-05-24 16:54:26 +02:00
else
{
2009-01-06 06:53:34 +01:00
map_in_out(request, message, in_blr_length, in_blr, in_msg_length, NULL, in_msg);
2008-02-28 14:48:16 +01:00
2009-01-06 06:53:34 +01:00
JRD_start_and_send(tdbb, request->req_request, request->req_transaction, message->msg_number,
message->msg_length, reinterpret_cast<SCHAR*>(message->msg_buffer),
0);
2001-05-23 15:26:42 +02:00
}
// REQ_EXEC_BLOCK has no outputs so there are no out_msg
// supplied from client side, but REQ_EXEC_BLOCK requires
// 2-byte message for EOS synchronization
2005-01-12 05:20:17 +01:00
const bool isBlock = (request->req_type == REQ_EXEC_BLOCK);
message = request->req_receive;
2008-02-28 14:48:16 +01:00
if ((out_msg_length && message) || isBlock)
{
char temp_buffer[FB_DOUBLE_ALIGN * 2];
2008-02-28 14:48:16 +01:00
dsql_msg temp_msg;
2001-05-23 15:26:42 +02:00
/* Insure that the blr for the message is parsed, regardless of
whether anything is found by the call to receive. */
if (out_msg_length && out_blr_length) {
2009-01-06 06:53:34 +01:00
parse_blr(out_blr_length, out_blr, out_msg_length, message->msg_parameters);
2008-12-05 02:20:14 +01:00
}
else if (!out_msg_length && isBlock) {
message = &temp_msg;
message->msg_number = 1;
message->msg_length = 2;
2008-11-06 01:23:21 +01:00
message->msg_buffer = (UCHAR*) FB_ALIGN((U_IPTR) temp_buffer, FB_DOUBLE_ALIGN);
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
JRD_receive(tdbb, request->req_request, message->msg_number, message->msg_length,
message->msg_buffer, 0);
2001-05-23 15:26:42 +02:00
if (out_msg_length)
map_in_out(NULL, message, 0, out_blr, out_msg_length, out_msg);
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// if this is a singleton select, make sure there's in fact one record
2001-05-23 15:26:42 +02:00
2001-05-24 16:54:26 +02:00
if (singleton)
{
2001-05-23 15:26:42 +02:00
USHORT counter;
/* Create a temp message buffer and try two more receives.
If both succeed then the first is the next record and the
second is either another record or the end of record message.
In either case, there's more than one record. */
2009-01-06 06:53:34 +01:00
UCHAR* message_buffer = (UCHAR*) gds__alloc((ULONG) message->msg_length);
2001-05-24 16:54:26 +02:00
2008-02-28 14:48:16 +01:00
ISC_STATUS status = FB_SUCCESS;
2008-02-01 21:18:11 +01:00
for (counter = 0; counter < 2 && !status; counter++)
{
ThreadStatusGuard local_status(tdbb);
2008-02-01 21:18:11 +01:00
try
2008-02-01 21:18:11 +01:00
{
JRD_receive(tdbb, request->req_request, message->msg_number,
message->msg_length, message_buffer, 0);
status = FB_SUCCESS;
2008-02-01 21:18:11 +01:00
}
catch (Firebird::Exception&)
{
status = tdbb->tdbb_status_vector[1];
}
2001-05-23 15:26:42 +02:00
}
2008-02-01 21:18:11 +01:00
gds__free(message_buffer);
2001-05-23 15:26:42 +02:00
/* two successful receives means more than one record
a req_sync error on the first pass above means no records
a non-req_sync error on any of the passes above is an error */
2008-02-28 14:48:16 +01:00
if (!status)
2001-05-24 16:54:26 +02:00
{
2008-02-28 14:48:16 +01:00
tdbb->tdbb_status_vector[0] = isc_arg_gds;
tdbb->tdbb_status_vector[1] = isc_sing_select_err;
tdbb->tdbb_status_vector[2] = isc_arg_end;
2003-11-08 00:27:24 +01:00
return_status = isc_sing_select_err;
2001-05-23 15:26:42 +02:00
}
2008-02-28 14:48:16 +01:00
else if (status == isc_req_sync && counter == 1)
2001-05-24 16:54:26 +02:00
{
2008-02-28 14:48:16 +01:00
tdbb->tdbb_status_vector[0] = isc_arg_gds;
tdbb->tdbb_status_vector[1] = isc_stream_eof;
tdbb->tdbb_status_vector[2] = isc_arg_end;
2003-11-08 00:27:24 +01:00
return_status = isc_stream_eof;
2001-05-23 15:26:42 +02:00
}
2008-02-28 14:48:16 +01:00
else if (status != isc_req_sync)
2001-05-24 16:54:26 +02:00
{
2008-02-28 14:48:16 +01:00
Firebird::status_exception::raise(tdbb->tdbb_status_vector);
2001-05-24 16:54:26 +02:00
}
2001-05-23 15:26:42 +02:00
}
}
2008-02-28 14:48:16 +01:00
UCHAR buffer[20]; // Not used after retrieved
if (request->req_type == REQ_UPDATE_CURSOR)
2001-05-24 16:54:26 +02:00
{
2009-01-06 06:53:34 +01:00
sql_info(tdbb, request, sizeof(sql_records_info), sql_records_info, sizeof(buffer), buffer);
if (!request->req_updates)
2001-05-24 16:54:26 +02:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) <<
Arg::Gds(isc_deadlock) <<
Arg::Gds(isc_update_conflict));
2001-05-23 15:26:42 +02:00
}
}
else if (request->req_type == REQ_DELETE_CURSOR)
{
2009-01-06 06:53:34 +01:00
sql_info(tdbb, request, sizeof(sql_records_info), sql_records_info, sizeof(buffer), buffer);
if (!request->req_deletes)
2001-05-24 16:54:26 +02:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-913) <<
Arg::Gds(isc_deadlock) <<
Arg::Gds(isc_update_conflict));
2001-05-23 15:26:42 +02:00
}
}
}
/**
2008-12-05 02:20:14 +01:00
filter_sub_type
2008-12-05 02:20:14 +01:00
@brief Determine the sub_type to use in filtering
a blob.
2008-12-05 02:20:14 +01:00
@param request
@param node
**/
static SSHORT filter_sub_type( dsql_req* request, const dsql_nod* node)
2001-05-23 15:26:42 +02:00
{
if (node->nod_type == nod_constant)
return (SSHORT) node->getSlong();
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
const dsql_par* parameter = (dsql_par*) node->nod_arg[e_par_parameter];
const dsql_par* null = parameter->par_null;
if (null)
2008-03-02 09:40:09 +01:00
{
2001-05-23 15:26:42 +02:00
if (*((SSHORT *) null->par_desc.dsc_address))
return 0;
2008-03-02 09:40:09 +01:00
}
2001-05-23 15:26:42 +02:00
return *((SSHORT *) parameter->par_desc.dsc_address);
}
/**
2008-12-05 02:20:14 +01:00
get_indices
2008-12-05 02:20:14 +01:00
@brief Retrieve the indices from the index tree in
the request info buffer (explain_ptr), and print them out
in the plan buffer. Return true on success and false on failure.
2008-12-05 02:20:14 +01:00
@param explain_length_ptr
@param explain_ptr
@param plan_length_ptr
@param plan_ptr
**/
2009-01-06 06:53:34 +01:00
static bool get_indices(SSHORT* explain_length_ptr, const SCHAR** explain_ptr,
SSHORT* plan_length_ptr, SCHAR** plan_ptr)
2001-05-23 15:26:42 +02:00
{
USHORT length;
SSHORT explain_length = *explain_length_ptr;
const SCHAR* explain = *explain_ptr;
SSHORT plan_length = *plan_length_ptr;
SCHAR* plan = *plan_ptr;
2001-05-23 15:26:42 +02:00
/* go through the index tree information, just
extracting the indices used */
explain_length--;
2009-01-06 06:53:34 +01:00
switch (*explain++)
{
2003-11-08 00:27:24 +01:00
case isc_info_rsb_and:
case isc_info_rsb_or:
if (!get_indices(&explain_length, &explain, &plan_length, &plan))
return false;
if (!get_indices(&explain_length, &explain, &plan_length, &plan))
return false;
2001-05-23 15:26:42 +02:00
break;
2003-11-08 00:27:24 +01:00
case isc_info_rsb_dbkey:
2001-05-23 15:26:42 +02:00
break;
2003-11-08 00:27:24 +01:00
case isc_info_rsb_index:
2001-05-23 15:26:42 +02:00
explain_length--;
length = *explain++;
2008-12-05 02:20:14 +01:00
// if this isn't the first index, put out a comma
2001-05-23 15:26:42 +02:00
if (plan[-1] != '(' && plan[-1] != ' ') {
plan_length -= 2;
if (plan_length < 0)
return false;
2001-05-23 15:26:42 +02:00
*plan++ = ',';
*plan++ = ' ';
2001-05-23 15:26:42 +02:00
}
2008-12-05 02:20:14 +01:00
// now put out the index name
2001-05-23 15:26:42 +02:00
if ((plan_length -= length) < 0)
return false;
2001-05-23 15:26:42 +02:00
explain_length -= length;
while (length--)
*plan++ = *explain++;
break;
default:
return false;
2001-05-23 15:26:42 +02:00
}
*explain_length_ptr = explain_length;
*explain_ptr = explain;
*plan_length_ptr = plan_length;
*plan_ptr = plan;
return true;
2001-05-23 15:26:42 +02:00
}
/**
2008-12-05 02:20:14 +01:00
get_plan_info
2008-12-05 02:20:14 +01:00
@brief Get the access plan for the request and turn
it into a textual representation suitable for
human reading.
2008-12-05 02:20:14 +01:00
@param request
@param buffer_length
@param buffer
**/
2008-02-28 14:48:16 +01:00
static USHORT get_plan_info(thread_db* tdbb,
dsql_req* request,
SSHORT buffer_length,
SCHAR** out_buffer)
2001-05-23 15:26:42 +02:00
{
2008-04-18 03:37:44 +02:00
if (!request->req_request) // DDL
return 0;
Firebird::HalfStaticArray<SCHAR, BUFFER_LARGE> explain_buffer;
explain_buffer.resize(BUFFER_LARGE);
SCHAR* buffer_ptr = *out_buffer;
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// get the access path info for the underlying request from the engine
2001-05-23 15:26:42 +02:00
try
{
JRD_request_info(tdbb, request->req_request, 0,
sizeof(explain_info), explain_info,
explain_buffer.getCount(), explain_buffer.begin());
2001-05-23 15:26:42 +02:00
if (explain_buffer[0] == isc_info_truncated)
{
explain_buffer.resize(MAX_SSHORT);
2001-05-23 15:26:42 +02:00
JRD_request_info(tdbb, request->req_request, 0,
sizeof(explain_info), explain_info,
explain_buffer.getCount(), explain_buffer.begin());
if (explain_buffer[0] == isc_info_truncated)
{
return 0;
}
}
2001-05-23 15:26:42 +02:00
}
catch (Firebird::Exception&)
{
return 0;
}
2001-05-23 15:26:42 +02:00
SCHAR* plan;
for (int i = 0; i < 2; i++)
{
const SCHAR* explain = explain_buffer.begin();
2003-11-08 00:27:24 +01:00
if (*explain++ != isc_info_access_path)
{
2001-05-23 15:26:42 +02:00
return 0;
}
2001-05-23 15:26:42 +02:00
SSHORT explain_length = (UCHAR) *explain++;
2001-05-23 15:26:42 +02:00
explain_length += (UCHAR) (*explain++) << 8;
plan = buffer_ptr;
2004-10-07 10:27:45 +02:00
/* CVC: What if we need to do 2nd pass? Those variables were only initialized
at the begining of the function hence they had trash the second time. */
USHORT join_count = 0, level = 0;
2002-06-29 08:56:51 +02:00
2008-12-05 02:20:14 +01:00
// keep going until we reach the end of the explain info
2001-05-23 15:26:42 +02:00
while (explain_length > 0 && buffer_length > 0)
{
2009-01-06 06:53:34 +01:00
if (!get_rsb_item(&explain_length, &explain, &buffer_length, &plan, &join_count, &level))
{
// don't allocate buffer of the same length second time
// and let user know plan is incomplete
if (buffer_ptr != *out_buffer) {
if (buffer_length < 3) {
plan -= 3 - buffer_length;
}
*plan++ = '.';
*plan++ = '.';
*plan++ = '.';
break;
}
2008-12-05 02:20:14 +01:00
// assume we have run out of room in the buffer, try again with a larger one
const size_t new_length = MAX_SSHORT;
2008-10-16 10:51:51 +02:00
char* const temp = static_cast<char*>(gds__alloc(new_length));
if (!temp) {
// NOMEM. Do not attempt one more try
i++;
continue;
}
2009-01-06 06:53:34 +01:00
buffer_ptr = temp;
buffer_length = (SSHORT) new_length;
2001-05-23 15:26:42 +02:00
break;
}
}
2001-05-23 15:26:42 +02:00
if (buffer_ptr == *out_buffer)
2001-05-23 15:26:42 +02:00
break;
}
*out_buffer = buffer_ptr;
return plan - *out_buffer;
2001-05-23 15:26:42 +02:00
}
/**
2008-12-05 02:20:14 +01:00
get_request_info
2008-12-05 02:20:14 +01:00
@brief Get the records updated/deleted for record
2008-12-05 02:20:14 +01:00
@param request
@param buffer_length
@param buffer
**/
2008-02-28 14:48:16 +01:00
static USHORT get_request_info(thread_db* tdbb,
dsql_req* request,
SSHORT buffer_length,
UCHAR* buffer)
2001-05-23 15:26:42 +02:00
{
if (!request->req_request) // DDL
return 0;
2008-12-05 02:20:14 +01:00
// get the info for the request from the engine
2001-05-23 15:26:42 +02:00
try
{
JRD_request_info(tdbb, request->req_request, 0,
sizeof(record_info), record_info,
buffer_length, reinterpret_cast<char*>(buffer));
}
catch (Firebird::Exception&)
{
return 0;
2008-02-01 21:18:11 +01:00
}
2001-05-23 15:26:42 +02:00
2004-09-09 11:24:42 +02:00
const UCHAR* data = buffer;
2001-05-23 15:26:42 +02:00
request->req_updates = request->req_deletes = 0;
request->req_selects = request->req_inserts = 0;
UCHAR p;
while ((p = *data++) != isc_info_end)
{
2009-01-06 06:53:34 +01:00
const USHORT data_length = static_cast<USHORT>(gds__vax_integer(data, 2));
2001-05-23 15:26:42 +02:00
data += 2;
2009-01-06 06:53:34 +01:00
switch (p)
{
2003-11-08 00:27:24 +01:00
case isc_info_req_update_count:
2004-09-09 11:24:42 +02:00
request->req_updates = gds__vax_integer(data, data_length);
2001-05-23 15:26:42 +02:00
break;
2003-11-08 00:27:24 +01:00
case isc_info_req_delete_count:
2004-09-09 11:24:42 +02:00
request->req_deletes = gds__vax_integer(data, data_length);
2001-05-23 15:26:42 +02:00
break;
2003-11-08 00:27:24 +01:00
case isc_info_req_select_count:
2004-09-09 11:24:42 +02:00
request->req_selects = gds__vax_integer(data, data_length);
2001-05-23 15:26:42 +02:00
break;
2003-11-08 00:27:24 +01:00
case isc_info_req_insert_count:
2004-09-09 11:24:42 +02:00
request->req_inserts = gds__vax_integer(data, data_length);
2001-05-23 15:26:42 +02:00
break;
default:
break;
}
2001-05-23 15:26:42 +02:00
data += data_length;
}
return data - buffer;
}
/**
2008-12-05 02:20:14 +01:00
get_rsb_item
2008-12-05 02:20:14 +01:00
@brief Use recursion to print out a reverse-polish
access plan of joins and join types. Return true on success
and false on failure
2008-12-05 02:20:14 +01:00
@param explain_length_ptr
@param explain_ptr
@param plan_length_ptr
@param plan_ptr
@param parent_join_count
@param level_ptr
**/
2003-09-04 23:26:15 +02:00
static bool get_rsb_item(SSHORT* explain_length_ptr,
const SCHAR** explain_ptr,
2001-12-24 03:51:06 +01:00
SSHORT* plan_length_ptr,
SCHAR** plan_ptr,
USHORT* parent_join_count,
USHORT* level_ptr)
2001-05-23 15:26:42 +02:00
{
const SCHAR* p;
SSHORT rsb_type;
2001-05-23 15:26:42 +02:00
SSHORT explain_length = *explain_length_ptr;
const SCHAR* explain = *explain_ptr;
SSHORT plan_length = *plan_length_ptr;
SCHAR* plan = *plan_ptr;
2001-05-23 15:26:42 +02:00
explain_length--;
switch (*explain++)
{
2003-11-08 00:27:24 +01:00
case isc_info_rsb_begin:
2001-05-23 15:26:42 +02:00
if (!*level_ptr) {
2008-12-05 02:20:14 +01:00
// put out the PLAN prefix
2001-05-23 15:26:42 +02:00
p = "\nPLAN ";
if ((plan_length -= strlen(p)) < 0)
return false;
2001-05-23 15:26:42 +02:00
while (*p)
*plan++ = *p++;
}
(*level_ptr)++;
break;
2003-11-08 00:27:24 +01:00
case isc_info_rsb_end:
2004-10-07 10:27:45 +02:00
if (*level_ptr) {
(*level_ptr)--;
}
/* else --*parent_join_count; ??? */
2001-05-23 15:26:42 +02:00
break;
2003-11-08 00:27:24 +01:00
case isc_info_rsb_relation:
2001-05-23 15:26:42 +02:00
/* for the single relation case, initiate
2001-05-23 15:26:42 +02:00
the relation with a parenthesis */
if (!*parent_join_count) {
if (--plan_length < 0)
return false;
2001-05-23 15:26:42 +02:00
*plan++ = '(';
}
2008-12-05 02:20:14 +01:00
// if this isn't the first relation, put out a comma
2001-05-23 15:26:42 +02:00
if (plan[-1] != '(') {
plan_length -= 2;
if (plan_length < 0)
return false;
2001-05-23 15:26:42 +02:00
*plan++ = ',';
*plan++ = ' ';
2001-05-23 15:26:42 +02:00
}
2008-12-05 02:20:14 +01:00
// put out the relation name
{ // scope to keep length local.
explain_length--;
2009-01-06 06:53:34 +01:00
SSHORT length = (UCHAR) *explain++;
explain_length -= length;
if ((plan_length -= length) < 0)
return false;
while (length--)
*plan++ = *explain++;
} // scope
2001-05-23 15:26:42 +02:00
break;
2003-11-08 00:27:24 +01:00
case isc_info_rsb_type:
2001-05-23 15:26:42 +02:00
explain_length--;
switch (rsb_type = *explain++)
{
2001-05-23 15:26:42 +02:00
/* for stream types which have multiple substreams, print out
the stream type and recursively print out the substreams so
we will know where to put the parentheses */
2003-11-08 00:27:24 +01:00
case isc_info_rsb_union:
case isc_info_rsb_recursive:
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// put out all the substreams of the join
{ // scope to have union_count, union_level and union_join_count local.
explain_length--;
USHORT union_count = (USHORT) * explain++ - 1;
2001-05-23 15:26:42 +02:00
// finish the first union member
2001-05-23 15:26:42 +02:00
USHORT union_level = *level_ptr;
USHORT union_join_count = 0;
2009-01-06 06:53:34 +01:00
while (explain_length > 0 && plan_length > 0)
{
if (!get_rsb_item(&explain_length, &explain, &plan_length, &plan,
&union_join_count, &union_level))
2003-09-04 23:26:15 +02:00
{
return false;
2003-09-04 23:26:15 +02:00
}
if (union_level == *level_ptr)
2001-05-23 15:26:42 +02:00
break;
}
/* for the rest of the members, start the level at 0 so each
gets its own "PLAN ... " line */
2009-01-06 06:53:34 +01:00
while (union_count)
{
union_join_count = 0;
union_level = 0;
2009-01-06 06:53:34 +01:00
while (explain_length > 0 && plan_length > 0)
{
if (!get_rsb_item(&explain_length, &explain, &plan_length,
&plan, &union_join_count, &union_level))
{
return false;
}
if (!union_level)
break;
}
union_count--;
}
} // scope
2001-05-23 15:26:42 +02:00
break;
2003-11-08 00:27:24 +01:00
case isc_info_rsb_cross:
case isc_info_rsb_left_cross:
case isc_info_rsb_merge:
2001-05-23 15:26:42 +02:00
/* if this join is itself part of a join list,
2001-05-23 15:26:42 +02:00
but not the first item, then put out a comma */
2009-01-06 06:53:34 +01:00
if (*parent_join_count && plan[-1] != '(')
{
plan_length -= 2;
if (plan_length < 0)
return false;
2001-05-23 15:26:42 +02:00
*plan++ = ',';
*plan++ = ' ';
2001-05-23 15:26:42 +02:00
}
2008-12-05 02:20:14 +01:00
// put out the join type
2001-05-23 15:26:42 +02:00
2009-01-06 06:53:34 +01:00
if (rsb_type == isc_info_rsb_cross || rsb_type == isc_info_rsb_left_cross)
2004-09-09 10:56:33 +02:00
{
2004-10-07 10:27:45 +02:00
p = "JOIN (";
}
2002-06-29 08:56:51 +02:00
else {
2001-05-23 15:26:42 +02:00
p = "MERGE (";
2004-10-07 10:27:45 +02:00
}
2001-05-23 15:26:42 +02:00
if ((plan_length -= strlen(p)) < 0)
return false;
2001-05-23 15:26:42 +02:00
while (*p)
*plan++ = *p++;
2008-12-05 02:20:14 +01:00
// put out all the substreams of the join
2001-05-23 15:26:42 +02:00
explain_length--;
{ // scope to have join_count local.
USHORT join_count = (USHORT) * explain++;
2009-01-06 06:53:34 +01:00
while (join_count && explain_length > 0 && plan_length > 0)
{
if (!get_rsb_item(&explain_length, &explain, &plan_length,
&plan, &join_count, level_ptr))
{
return false;
}
// CVC: Here's the additional stop condition.
if (!*level_ptr) {
break;
}
2003-09-04 23:26:15 +02:00
}
} // scope
2002-06-29 08:56:51 +02:00
2008-12-05 02:20:14 +01:00
// put out the final parenthesis for the join
2001-05-23 15:26:42 +02:00
if (--plan_length < 0)
return false;
2009-01-06 06:53:34 +01:00
*plan++ = ')';
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// this qualifies as a stream, so decrement the join count
2001-05-23 15:26:42 +02:00
if (*parent_join_count)
-- * parent_join_count;
break;
2003-11-08 00:27:24 +01:00
case isc_info_rsb_indexed:
case isc_info_rsb_navigate:
case isc_info_rsb_sequential:
case isc_info_rsb_ext_sequential:
case isc_info_rsb_ext_indexed:
2006-07-23 14:14:59 +02:00
case isc_info_rsb_virt_sequential:
2008-01-16 07:52:43 +01:00
switch (rsb_type)
2003-09-04 23:26:15 +02:00
{
2008-01-16 07:52:43 +01:00
case isc_info_rsb_indexed:
case isc_info_rsb_ext_indexed:
2003-09-04 23:26:15 +02:00
p = " INDEX (";
2008-01-16 07:52:43 +01:00
break;
case isc_info_rsb_navigate:
2001-05-23 15:26:42 +02:00
p = " ORDER ";
2008-01-16 07:52:43 +01:00
break;
default:
2001-05-23 15:26:42 +02:00
p = " NATURAL";
2008-01-16 07:52:43 +01:00
}
2001-05-23 15:26:42 +02:00
if ((plan_length -= strlen(p)) < 0)
return false;
2001-05-23 15:26:42 +02:00
while (*p)
*plan++ = *p++;
2008-12-05 02:20:14 +01:00
// print out additional index information
2001-05-23 15:26:42 +02:00
2009-01-06 06:53:34 +01:00
if (rsb_type == isc_info_rsb_indexed || rsb_type == isc_info_rsb_navigate ||
2004-09-09 10:56:33 +02:00
rsb_type == isc_info_rsb_ext_indexed)
{
if (!get_indices(&explain_length, &explain, &plan_length, &plan))
return false;
2001-05-23 15:26:42 +02:00
}
2009-01-06 06:53:34 +01:00
if (rsb_type == isc_info_rsb_navigate && *explain == isc_info_rsb_indexed)
{
2004-10-07 10:27:45 +02:00
USHORT idx_count = 1;
2009-01-06 06:53:34 +01:00
if (!get_rsb_item(&explain_length, &explain, &plan_length, &plan, &idx_count, level_ptr))
{
return false;
}
}
2009-01-06 06:53:34 +01:00
if (rsb_type == isc_info_rsb_indexed || rsb_type == isc_info_rsb_ext_indexed)
2004-09-09 10:56:33 +02:00
{
2001-05-23 15:26:42 +02:00
if (--plan_length < 0)
return false;
2001-05-23 15:26:42 +02:00
*plan++ = ')';
}
2008-12-05 02:20:14 +01:00
// detect the end of a single relation and put out a final parenthesis
2001-05-23 15:26:42 +02:00
if (!*parent_join_count)
2009-01-06 06:53:34 +01:00
{
2001-05-23 15:26:42 +02:00
if (--plan_length < 0)
return false;
2009-01-06 06:53:34 +01:00
*plan++ = ')';
}
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// this also qualifies as a stream, so decrement the join count
2001-05-23 15:26:42 +02:00
if (*parent_join_count)
-- * parent_join_count;
break;
2003-11-08 00:27:24 +01:00
case isc_info_rsb_sort:
2001-05-23 15:26:42 +02:00
/* if this sort is on behalf of a union, don't bother to
print out the sort, because unions handle the sort on all
substreams at once, and a plan maps to each substream
2001-05-23 15:26:42 +02:00
in the union, so the sort doesn't really apply to a particular plan */
2009-01-06 06:53:34 +01:00
if (explain_length > 2 && (explain[0] == isc_info_rsb_begin) &&
(explain[1] == isc_info_rsb_type) && (explain[2] == isc_info_rsb_union))
2003-09-04 23:26:15 +02:00
{
break;
}
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// if this isn't the first item in the list, put out a comma
2001-05-23 15:26:42 +02:00
2009-01-06 06:53:34 +01:00
if (*parent_join_count && plan[-1] != '(')
{
plan_length -= 2;
if (plan_length < 0)
return false;
2001-05-23 15:26:42 +02:00
*plan++ = ',';
*plan++ = ' ';
2001-05-23 15:26:42 +02:00
}
p = "SORT (";
if ((plan_length -= strlen(p)) < 0)
return false;
2001-05-23 15:26:42 +02:00
while (*p)
*plan++ = *p++;
/* the rsb_sort should always be followed by a begin...end block,
2001-05-23 15:26:42 +02:00
allowing us to include everything inside the sort in parentheses */
{ // scope to have save_level local.
const USHORT save_level = *level_ptr;
2009-01-06 06:53:34 +01:00
while (explain_length > 0 && plan_length > 0)
{
if (!get_rsb_item(&explain_length, &explain, &plan_length,
&plan, parent_join_count, level_ptr))
{
return false;
}
if (*level_ptr == save_level)
break;
2004-09-09 10:56:33 +02:00
}
2001-05-23 15:26:42 +02:00
if (--plan_length < 0)
return false;
*plan++ = ')';
} // scope
2001-05-23 15:26:42 +02:00
break;
default:
break;
} // switch (rsb_type = *explain++)
2001-05-23 15:26:42 +02:00
break;
default:
break;
}
*explain_length_ptr = explain_length;
*explain_ptr = explain;
*plan_length_ptr = plan_length;
*plan_ptr = plan;
return true;
2001-05-23 15:26:42 +02:00
}
/**
2008-12-05 02:20:14 +01:00
init
2008-12-05 02:20:14 +01:00
@brief Initialize dynamic SQL. This is called only once.
2008-12-05 02:20:14 +01:00
@param db_handle
**/
2008-02-28 14:48:16 +01:00
static dsql_dbb* init(Attachment* attachment)
2001-05-23 15:26:42 +02:00
{
thread_db* tdbb = JRD_get_thread_data();
2008-02-28 14:48:16 +01:00
if (!attachment->att_dsql_instance)
{
MemoryPool& pool = *attachment->att_database->createPool();
dsql_dbb* const database = FB_NEW(pool) dsql_dbb(pool);
database->dbb_attachment = attachment;
database->dbb_database = attachment->att_database;
attachment->att_dsql_instance = database;
SCHAR buffer[BUFFER_TINY];
try
{
ThreadStatusGuard status_vector(tdbb);
INF_database_info(db_hdr_info_items, sizeof(db_hdr_info_items), buffer, sizeof(buffer));
}
catch (Firebird::Exception&)
{
return database;
2001-05-23 15:26:42 +02:00
}
2008-02-28 14:48:16 +01:00
const UCHAR* data = reinterpret_cast<UCHAR*>(buffer);
UCHAR p;
while ((p = *data++) != isc_info_end)
{
const SSHORT l = static_cast<SSHORT>(gds__vax_integer(data, 2));
data += 2;
2008-02-28 14:48:16 +01:00
switch (p)
{
case isc_info_db_sql_dialect:
database->dbb_db_SQL_dialect = (USHORT) data[0];
break;
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
case isc_info_ods_version:
database->dbb_ods_version = gds__vax_integer(data, l);
if (database->dbb_ods_version <= 7)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-804) <<
Arg::Gds(isc_dsql_too_old_ods) << Arg::Num(8));
2008-02-28 14:48:16 +01:00
}
break;
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
case isc_info_ods_minor_version:
database->dbb_minor_version = gds__vax_integer(data, l);
break;
2008-02-01 21:18:11 +01:00
2008-02-28 14:48:16 +01:00
// This flag indicates the version level of the engine
// itself, so we can tell what capabilities the engine
// code itself (as opposed to the on-disk structure).
// Apparently the base level up to now indicated the major
// version number, but for 4.1 the base level is being
// incremented, so the base level indicates an engine version
// as follows:
// 1 == v1.x
// 2 == v2.x
// 3 == v3.x
// 4 == v4.0 only
// 5 == v4.1
// Note: this info item is so old it apparently uses an
// archaic format, not a standard vax integer format.
case isc_info_base_level:
database->dbb_base_level = (USHORT) data[1];
break;
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
case isc_info_db_read_only:
database->dbb_read_only = (USHORT) data[0] ? true : false;
break;
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
default:
break;
}
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
data += l;
2001-05-23 15:26:42 +02:00
}
}
2008-02-28 14:48:16 +01:00
return attachment->att_dsql_instance;
2001-05-23 15:26:42 +02:00
}
/**
2008-12-05 02:20:14 +01:00
map_in_out
2008-12-05 02:20:14 +01:00
@brief Map data from external world into message or
from message to external world.
2008-12-05 02:20:14 +01:00
@param request
@param message
@param blr_length
@param blr
@param msg_length
@param dsql_msg_buf
@param in_dsql_msg_buf
**/
static void map_in_out( dsql_req* request,
dsql_msg* message,
2001-05-23 15:26:42 +02:00
USHORT blr_length,
2003-10-16 10:51:06 +02:00
const UCHAR* blr,
2001-05-23 15:26:42 +02:00
USHORT msg_length,
UCHAR* dsql_msg_buf,
const UCHAR* in_dsql_msg_buf)
2001-05-23 15:26:42 +02:00
{
USHORT count = parse_blr(blr_length, blr, msg_length, message->msg_parameters);
2001-05-23 15:26:42 +02:00
// When mapping data from the external world, request will be non-NULL.
// When mapping data from an internal message, request will be NULL.
dsql_par* parameter;
2001-05-23 15:26:42 +02:00
2009-01-06 06:53:34 +01:00
for (parameter = message->msg_parameters; parameter; parameter = parameter->par_next)
{
if (parameter->par_index)
{
2008-12-05 02:20:14 +01:00
// Make sure the message given to us is long enough
2001-05-23 15:26:42 +02:00
2009-01-06 06:53:34 +01:00
dsc desc = parameter->par_user_desc;
USHORT length = (IPTR) desc.dsc_address + desc.dsc_length;
2001-05-23 15:26:42 +02:00
if (length > msg_length)
break;
if (!desc.dsc_dtype)
break;
2001-05-23 15:26:42 +02:00
SSHORT* flag = NULL;
dsql_par* const null_ind = parameter->par_null;
if (null_ind != NULL)
{
const USHORT null_offset = (IPTR) null_ind->par_user_desc.dsc_address;
2001-05-23 15:26:42 +02:00
length = null_offset + sizeof(SSHORT);
if (length > msg_length)
break;
if (!request) {
flag = reinterpret_cast<SSHORT*>(dsql_msg_buf + null_offset);
*flag = *reinterpret_cast<const SSHORT*>(null_ind->par_desc.dsc_address);
2001-05-23 15:26:42 +02:00
}
else {
flag = reinterpret_cast<SSHORT*>(null_ind->par_desc.dsc_address);
*flag = *reinterpret_cast<const SSHORT*>(in_dsql_msg_buf + null_offset);
2001-05-23 15:26:42 +02:00
}
}
2001-05-23 15:26:42 +02:00
if (!request)
{
if (!flag || *flag >= 0)
{
desc.dsc_address = dsql_msg_buf + (IPTR) desc.dsc_address;
MOVD_move(&parameter->par_desc, &desc);
}
}
2001-05-23 15:26:42 +02:00
else if (!flag || *flag >= 0)
{
2009-01-29 21:36:29 +01:00
// Safe cast because desc is used as source only.
desc.dsc_address = const_cast<UCHAR*>(in_dsql_msg_buf) + (IPTR) desc.dsc_address;
MOVD_move(&desc, &parameter->par_desc);
}
else if (parameter->par_desc.isBlob())
memset(parameter->par_desc.dsc_address, 0, parameter->par_desc.dsc_length);
2001-05-23 15:26:42 +02:00
count--;
}
}
2001-05-23 15:26:42 +02:00
/* If we got here because the loop was exited early or if part of the
message given to us hasn't been used, complain. */
if (parameter || count)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-804) <<
Arg::Gds(isc_dsql_sqlda_err));
}
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
dsql_par* dbkey;
2009-01-06 06:53:34 +01:00
if (request && ((dbkey = request->req_parent_dbkey) != NULL) &&
((parameter = request->req_dbkey) != NULL))
{
2001-05-23 15:26:42 +02:00
MOVD_move(&dbkey->par_desc, &parameter->par_desc);
dsql_par* null_ind = parameter->par_null;
if (null_ind != NULL)
{
SSHORT* flag = (SSHORT *) null_ind->par_desc.dsc_address;
2001-05-23 15:26:42 +02:00
*flag = 0;
}
}
2004-02-02 12:02:12 +01:00
dsql_par* rec_version;
2009-01-06 06:53:34 +01:00
if (request && ((rec_version = request->req_parent_rec_version) != NULL) &&
((parameter = request->req_rec_version) != NULL))
{
2001-05-23 15:26:42 +02:00
MOVD_move(&rec_version->par_desc, &parameter->par_desc);
dsql_par* null_ind = parameter->par_null;
if (null_ind != NULL)
{
SSHORT* flag = (SSHORT *) null_ind->par_desc.dsc_address;
2001-05-23 15:26:42 +02:00
*flag = 0;
}
}
}
/**
2008-12-05 02:20:14 +01:00
parse_blr
2008-12-05 02:20:14 +01:00
@brief Parse the message of a blr request.
2008-12-05 02:20:14 +01:00
@param blr_length
@param blr
@param msg_length
@param parameters
**/
2009-01-06 06:53:34 +01:00
static USHORT parse_blr(USHORT blr_length,
2004-02-02 12:02:12 +01:00
const UCHAR* blr, const USHORT msg_length, dsql_par* parameters)
2001-05-23 15:26:42 +02:00
{
/* If there's no blr length, then the format of the current message buffer
is identical to the format of the previous one. */
if (!blr_length)
{
2003-10-16 10:51:06 +02:00
USHORT par_count = 0;
2009-01-06 06:53:34 +01:00
for (const dsql_par* parameter = parameters; parameter; parameter = parameter->par_next)
{
if (parameter->par_index) {
2003-10-16 10:51:06 +02:00
++par_count;
}
}
2003-10-16 10:51:06 +02:00
return par_count;
2001-05-23 15:26:42 +02:00
}
if (*blr != blr_version4 && *blr != blr_version5)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-804) <<
Arg::Gds(isc_dsql_sqlda_err));
}
2008-12-05 02:20:14 +01:00
blr++; // skip the blr_version
2001-05-23 15:26:42 +02:00
if (*blr++ != blr_begin || *blr++ != blr_message)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-804) <<
Arg::Gds(isc_dsql_sqlda_err));
}
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
++blr; // skip the message number
2003-10-16 10:51:06 +02:00
USHORT count = *blr++;
2001-05-23 15:26:42 +02:00
count += (*blr++) << 8;
count /= 2;
2003-10-16 10:51:06 +02:00
USHORT offset = 0;
for (USHORT index = 1; index <= count; index++)
{
2003-10-16 10:51:06 +02:00
dsc desc;
2001-05-23 15:26:42 +02:00
desc.dsc_scale = 0;
desc.dsc_sub_type = 0;
desc.dsc_flags = 0;
2008-12-05 02:20:14 +01:00
switch (*blr++)
{
2001-05-23 15:26:42 +02:00
case blr_text:
desc.dsc_dtype = dtype_text;
desc.dsc_sub_type = ttype_dynamic;
desc.dsc_length = *blr++;
desc.dsc_length += (*blr++) << 8;
break;
case blr_varying:
desc.dsc_dtype = dtype_varying;
desc.dsc_sub_type = ttype_dynamic;
desc.dsc_length = *blr++ + sizeof(USHORT);
desc.dsc_length += (*blr++) << 8;
break;
case blr_text2:
desc.dsc_dtype = dtype_text;
desc.dsc_sub_type = *blr++;
desc.dsc_sub_type += (*blr++) << 8;
desc.dsc_length = *blr++;
desc.dsc_length += (*blr++) << 8;
break;
case blr_varying2:
desc.dsc_dtype = dtype_varying;
desc.dsc_sub_type = *blr++;
desc.dsc_sub_type += (*blr++) << 8;
desc.dsc_length = *blr++ + sizeof(USHORT);
desc.dsc_length += (*blr++) << 8;
break;
case blr_short:
desc.dsc_dtype = dtype_short;
desc.dsc_length = sizeof(SSHORT);
desc.dsc_scale = *blr++;
break;
case blr_long:
desc.dsc_dtype = dtype_long;
desc.dsc_length = sizeof(SLONG);
desc.dsc_scale = *blr++;
break;
case blr_int64:
desc.dsc_dtype = dtype_int64;
desc.dsc_length = sizeof(SINT64);
desc.dsc_scale = *blr++;
break;
case blr_quad:
desc.dsc_dtype = dtype_quad;
desc.dsc_length = sizeof(SLONG) * 2;
desc.dsc_scale = *blr++;
break;
case blr_float:
desc.dsc_dtype = dtype_real;
desc.dsc_length = sizeof(float);
break;
case blr_double:
case blr_d_float:
desc.dsc_dtype = dtype_double;
desc.dsc_length = sizeof(double);
break;
case blr_timestamp:
desc.dsc_dtype = dtype_timestamp;
desc.dsc_length = sizeof(SLONG) * 2;
break;
case blr_sql_date:
desc.dsc_dtype = dtype_sql_date;
desc.dsc_length = sizeof(SLONG);
break;
case blr_sql_time:
desc.dsc_dtype = dtype_sql_time;
desc.dsc_length = sizeof(SLONG);
break;
case blr_blob2:
{
desc.dsc_dtype = dtype_blob;
desc.dsc_length = sizeof(ISC_QUAD);
desc.dsc_sub_type = *blr++;
desc.dsc_sub_type += (*blr++) << 8;
USHORT textType = *blr++;
textType += (*blr++) << 8;
desc.setTextType(textType);
}
break;
2001-05-23 15:26:42 +02:00
default:
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-804) <<
Arg::Gds(isc_dsql_sqlda_err));
2001-05-23 15:26:42 +02:00
}
2003-10-16 10:51:06 +02:00
USHORT align = type_alignments[desc.dsc_dtype];
2001-05-23 15:26:42 +02:00
if (align)
offset = FB_ALIGN(offset, align);
desc.dsc_address = (UCHAR*)(IPTR) offset;
2001-05-23 15:26:42 +02:00
offset += desc.dsc_length;
if (*blr++ != blr_short || *blr++ != 0)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-804) <<
Arg::Gds(isc_dsql_sqlda_err));
}
2001-05-23 15:26:42 +02:00
align = type_alignments[dtype_short];
if (align)
offset = FB_ALIGN(offset, align);
2003-10-16 10:51:06 +02:00
USHORT null_offset = offset;
2001-05-23 15:26:42 +02:00
offset += sizeof(SSHORT);
2004-02-02 12:02:12 +01:00
for (dsql_par* parameter = parameters; parameter; parameter = parameter->par_next)
2003-09-13 14:16:48 +02:00
{
if (parameter->par_index == index) {
2001-05-23 15:26:42 +02:00
parameter->par_user_desc = desc;
2004-02-02 12:02:12 +01:00
dsql_par* null = parameter->par_null;
2003-10-16 10:51:06 +02:00
if (null) {
2001-05-23 15:26:42 +02:00
null->par_user_desc.dsc_dtype = dtype_short;
null->par_user_desc.dsc_scale = 0;
null->par_user_desc.dsc_length = sizeof(SSHORT);
null->par_user_desc.dsc_address = (UCHAR*)(IPTR) null_offset;
2001-05-23 15:26:42 +02:00
}
}
2003-09-13 14:16:48 +02:00
}
2001-05-23 15:26:42 +02:00
}
if (*blr++ != (UCHAR) blr_end || offset != msg_length)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-804) <<
Arg::Gds(isc_dsql_sqlda_err));
}
2001-05-23 15:26:42 +02:00
return count;
}
/**
2008-12-05 02:20:14 +01:00
prepare
2008-12-05 02:20:14 +01:00
@brief Prepare a statement for execution. Return SQL status
code. Note: caller is responsible for pool handling.
2008-12-05 02:20:14 +01:00
@param request
@param string_length
@param string
@param client_dialect
@param parser_version
**/
static dsql_req* prepare(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction,
2001-05-23 15:26:42 +02:00
USHORT string_length,
const TEXT* string,
2009-01-29 21:36:29 +01:00
USHORT client_dialect, USHORT parser_version)
2001-05-23 15:26:42 +02:00
{
2006-05-04 04:57:26 +02:00
ISC_STATUS_ARRAY local_status;
MOVE_CLEAR(local_status, sizeof(local_status));
2001-05-23 15:26:42 +02:00
if (client_dialect > SQL_DIALECT_CURRENT)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
Arg::Gds(isc_wish_list));
}
2001-05-23 15:26:42 +02:00
if (!string) {
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
// Unexpected end of command
// CVC: Nothing will be line 1, column 1 for the user.
Arg::Gds(isc_command_end_err2) << Arg::Num(1) << Arg::Num(1));
}
if (!string_length) {
2001-05-23 15:26:42 +02:00
string_length = strlen(string);
}
2001-05-23 15:26:42 +02:00
/* Get rid of the trailing ";" if there is one. */
for (const TEXT* p = string + string_length; p-- > string;)
{
2001-05-23 15:26:42 +02:00
if (*p != ' ') {
if (*p == ';')
string_length = p - string;
break;
}
}
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// allocate the statement block, then prepare the statement
Jrd::ContextPoolHolder context(tdbb, database->createPool());
MemoryPool& pool = *tdbb->getDefaultPool();
CompiledStatement* statement = FB_NEW(pool) CompiledStatement(pool);
statement->req_dbb = database;
statement->req_transaction = transaction;
statement->req_client_dialect = client_dialect;
try {
2008-12-05 02:20:14 +01:00
// Parse the SQL statement. If it croaks, return
2008-02-01 21:18:11 +01:00
Parser parser(*tdbb->getDefaultPool(), client_dialect, statement->req_dbb->dbb_db_SQL_dialect,
parser_version, string, string_length, tdbb->getAttachment()->att_charset);
2001-05-23 15:26:42 +02:00
dsql_nod* node = parser.parse();
2001-05-23 15:26:42 +02:00
if (!node)
{
// CVC: Apparently, dsql_ypparse won't return if the command is incomplete,
// because yyerror() will call ERRD_post().
// This may be a special case, but we don't know about positions here.
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
// Unexpected end of command
Arg::Gds(isc_command_end_err));
2001-05-23 15:26:42 +02:00
}
2008-12-05 02:20:14 +01:00
// allocate the send and receive messages
2001-05-23 15:26:42 +02:00
statement->req_send = FB_NEW(pool) dsql_msg;
2008-02-28 14:48:16 +01:00
dsql_msg* message = FB_NEW(pool) dsql_msg;
statement->req_receive = message;
2001-05-23 15:26:42 +02:00
message->msg_number = 1;
#ifdef SCROLLABLE_CURSORS
if (statement->req_dbb->dbb_base_level >= 5) {
/* allocate a message in which to send scrolling information
2001-05-23 15:26:42 +02:00
outside of the normal send/receive protocol */
statement->req_async = message = FB_NEW(*tdsql->getDefaultPool()) dsql_msg;
2001-05-23 15:26:42 +02:00
message->msg_number = 2;
}
#endif
statement->req_type = REQ_SELECT;
statement->req_flags &= ~REQ_cursor_open;
2001-05-23 15:26:42 +02:00
/*
* No work is done during pass1 for set transaction - like
* checking for valid table names. This is because that will
* require a valid transaction handle.
* Error will be caught at execute time.
*/
2001-05-23 15:26:42 +02:00
node = PASS1_statement(statement, node);
2001-05-23 15:26:42 +02:00
if (!node)
return statement;
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// stop here for statements not requiring code generation
2001-05-23 15:26:42 +02:00
if (statement->req_type == REQ_DDL && parser.isStmtAmbiguous() &&
statement->req_dbb->dbb_db_SQL_dialect != client_dialect)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-817) <<
Arg::Gds(isc_ddl_not_allowed_by_db_sql_dial) << Arg::Num(statement->req_dbb->dbb_db_SQL_dialect));
}
2001-05-23 15:26:42 +02:00
2009-01-06 06:53:34 +01:00
switch (statement->req_type)
{
2009-01-06 06:53:34 +01:00
case REQ_COMMIT:
case REQ_COMMIT_RETAIN:
case REQ_ROLLBACK:
case REQ_ROLLBACK_RETAIN:
return statement;
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// Work on blob segment statements
2009-01-06 06:53:34 +01:00
case REQ_GET_SEGMENT:
case REQ_PUT_SEGMENT:
GEN_port(statement, statement->req_blob->blb_open_in_msg);
GEN_port(statement, statement->req_blob->blb_open_out_msg);
GEN_port(statement, statement->req_blob->blb_segment_msg);
return statement;
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// Generate BLR, DDL or TPB for statement
// Start transactions takes parameters via a parameter block.
// The statement blr string is used for that
2009-01-06 06:53:34 +01:00
case REQ_START_TRANS:
GEN_start_transaction(statement, node);
return statement;
2001-05-23 15:26:42 +02:00
}
if (client_dialect > SQL_DIALECT_V5)
statement->req_flags |= REQ_blr_version5;
2001-05-23 15:26:42 +02:00
else
statement->req_flags |= REQ_blr_version4;
2005-05-22 05:11:41 +02:00
GEN_request(statement, node);
const USHORT length = statement->req_blr_data.getCount();
2008-12-05 02:20:14 +01:00
// stop here for ddl statements
2001-05-23 15:26:42 +02:00
2009-01-06 06:53:34 +01:00
if (statement->req_type == REQ_CREATE_DB || statement->req_type == REQ_DDL)
2008-02-28 14:48:16 +01:00
{
return statement;
2008-02-28 14:48:16 +01:00
}
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// have the access method compile the statement
2001-05-23 15:26:42 +02:00
#ifdef DSQL_DEBUG
if (DSQL_debug & 64) {
dsql_trace("Resulting BLR code for DSQL:");
gds__trace_raw("Statement:\n");
gds__trace_raw(string, string_length);
gds__trace_raw("\nBLR:\n");
gds__print_blr(statement->req_blr_data.begin(),
gds__trace_printer, 0, 0);
}
2001-05-23 15:26:42 +02:00
#endif
2008-12-05 02:20:14 +01:00
// check for warnings
2008-02-28 14:48:16 +01:00
if (tdbb->tdbb_status_vector[2] == isc_arg_warning) {
2008-12-05 02:20:14 +01:00
// save a status vector
2008-02-28 14:48:16 +01:00
memcpy(local_status, tdbb->tdbb_status_vector, sizeof(ISC_STATUS_ARRAY));
2001-05-23 15:26:42 +02:00
}
2008-02-28 14:48:16 +01:00
ISC_STATUS status = FB_SUCCESS;
2008-02-01 21:18:11 +01:00
try
{
2008-03-08 22:20:26 +01:00
JRD_compile(tdbb,
statement->req_dbb->dbb_attachment,
&statement->req_request,
2008-03-08 22:20:26 +01:00
length,
statement->req_blr_data.begin(),
2008-03-08 22:20:26 +01:00
string_length,
string,
statement->req_debug_data.getCount(),
statement->req_debug_data.begin());
}
catch (const Firebird::Exception&)
{
status = tdbb->tdbb_status_vector[1];
2008-02-01 21:18:11 +01:00
}
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// restore warnings (if there are any)
if (local_status[2] == isc_arg_warning)
{
2001-05-23 15:26:42 +02:00
int indx, len, warning;
2008-12-05 02:20:14 +01:00
// find end of a status vector
2008-02-28 14:48:16 +01:00
PARSE_STATUS(tdbb->tdbb_status_vector, indx, warning);
2001-05-23 15:26:42 +02:00
if (indx)
--indx;
2008-12-05 02:20:14 +01:00
// calculate length of saved warnings
2001-05-23 15:26:42 +02:00
PARSE_STATUS(local_status, len, warning);
len -= 2;
if ((len + indx - 1) < ISC_STATUS_LENGTH)
2008-02-28 14:48:16 +01:00
memcpy(&tdbb->tdbb_status_vector[indx], &local_status[2], sizeof(ISC_STATUS) * len);
2001-05-23 15:26:42 +02:00
}
// free blr memory
statement->req_blr_data.free();
2001-05-23 15:26:42 +02:00
if (status)
2008-02-28 14:48:16 +01:00
Firebird::status_exception::raise(tdbb->tdbb_status_vector);
2001-05-23 15:26:42 +02:00
return statement;
}
catch (const Firebird::Exception&)
{
release_request(tdbb, statement, true);
throw;
}
2001-05-23 15:26:42 +02:00
}
/**
2008-12-05 02:20:14 +01:00
put_item
2008-12-05 02:20:14 +01:00
@brief Put information item in output buffer if there is room, and
return an updated pointer. If there isn't room for the item,
indicate truncation and return NULL.
2008-12-05 02:20:14 +01:00
@param item
@param length
@param string
@param ptr
@param end
**/
static UCHAR* put_item( UCHAR item,
2009-01-06 06:53:34 +01:00
const USHORT length,
2003-10-16 10:51:06 +02:00
const UCHAR* string,
UCHAR* ptr,
2003-10-16 10:51:06 +02:00
const UCHAR* const end)
2001-05-23 15:26:42 +02:00
{
if (ptr + length + 3 >= end) {
2003-11-08 00:27:24 +01:00
*ptr = isc_info_truncated;
2001-05-23 15:26:42 +02:00
return NULL;
}
*ptr++ = item;
2009-01-06 06:53:34 +01:00
*ptr++ = (UCHAR) length;
2001-12-24 03:51:06 +01:00
*ptr++ = length >> 8;
2009-01-06 06:53:34 +01:00
if (length)
memcpy(ptr, string, length);
2001-05-23 15:26:42 +02:00
2009-01-06 06:53:34 +01:00
return ptr + length;
2001-05-23 15:26:42 +02:00
}
/**
2008-12-05 02:20:14 +01:00
release_request
2008-12-05 02:20:14 +01:00
@brief Release a dynamic request.
2008-12-05 02:20:14 +01:00
@param request
@param top_level
**/
2008-02-28 14:48:16 +01:00
static void release_request(thread_db* tdbb, dsql_req* request, bool drop)
2001-05-23 15:26:42 +02:00
{
SET_TDBB(tdbb);
2008-12-05 02:20:14 +01:00
2001-12-24 03:51:06 +01:00
// If request is parent, orphan the children and
// release a portion of their requests
2001-05-23 15:26:42 +02:00
for (dsql_req* child = request->req_offspring; child; child = child->req_sibling)
{
2001-05-23 15:26:42 +02:00
child->req_flags |= REQ_orphan;
child->req_parent = NULL;
2008-02-28 14:48:16 +01:00
Jrd::ContextPoolHolder context(tdbb, &child->req_pool);
release_request(tdbb, child, false);
2001-05-23 15:26:42 +02:00
}
// For requests that are linked to a parent, unlink it
2001-05-23 15:26:42 +02:00
if (request->req_parent)
2001-12-24 03:51:06 +01:00
{
dsql_req* parent = request->req_parent;
for (dsql_req** ptr = &parent->req_offspring; *ptr; ptr = &(*ptr)->req_sibling)
2001-12-24 03:51:06 +01:00
{
2001-05-23 15:26:42 +02:00
if (*ptr == request) {
*ptr = request->req_sibling;
break;
}
2001-12-24 03:51:06 +01:00
}
request->req_parent = NULL;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
// If the request had an open cursor, close it
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
if (request->req_flags & REQ_cursor_open) {
close_cursor(tdbb, request);
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
// If request is named, clear it from the hash table
2001-05-23 15:26:42 +02:00
if (request->req_name) {
HSHD_remove(request->req_name);
request->req_name = NULL;
}
if (request->req_cursor) {
HSHD_remove(request->req_cursor);
request->req_cursor = NULL;
}
// If a request has been compiled, release it now
2001-05-23 15:26:42 +02:00
if (request->req_request)
{
ThreadStatusGuard status_vector(tdbb);
try
{
CMP_release(tdbb, request->req_request);
request->req_request = NULL;
}
catch (Firebird::Exception&)
{
}
2001-05-23 15:26:42 +02:00
}
// free blr memory
request->req_blr_data.free();
// Release the entire request if explicitly asked for
2001-05-23 15:26:42 +02:00
if (drop)
2008-02-28 14:48:16 +01:00
{
request->req_dbb->deletePool(&request->req_pool);
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
}
/**
sql_info
@brief Return DSQL information buffer.
@param request
@param item_length
@param items
@param info_length
@param info
**/
2008-02-28 14:48:16 +01:00
static void sql_info(thread_db* tdbb,
dsql_req* request,
USHORT item_length,
const UCHAR* items,
USHORT info_length,
UCHAR* info)
{
2008-02-28 14:48:16 +01:00
if (!item_length || !items || !info_length || !info)
return;
UCHAR buffer[BUFFER_SMALL];
memset(buffer, 0, sizeof(buffer));
// Pre-initialize buffer. This is necessary because we don't want to transfer rubbish over the wire
2008-12-05 02:20:14 +01:00
memset(info, 0, info_length);
const UCHAR* const end_items = items + item_length;
const UCHAR* const end_info = info + info_length;
UCHAR *start_info;
if (*items == isc_info_length) {
start_info = info;
items++;
}
else {
2008-02-28 14:48:16 +01:00
start_info = NULL;
}
// CVC: Is it the idea that this pointer remains with its previous value
// in the loop or should it be made NULL in each iteration?
dsql_msg** message = NULL;
USHORT first_index = 0;
while (items < end_items && *items != isc_info_end)
{
USHORT length, number;
2008-03-10 10:32:43 +01:00
const UCHAR item = *items++;
2009-01-06 06:53:34 +01:00
switch (item)
{
2009-01-06 06:53:34 +01:00
case isc_info_sql_select:
case isc_info_sql_bind:
message = (item == isc_info_sql_select) ? &request->req_receive : &request->req_send;
if (info + 1 >= end_info) {
*info = isc_info_truncated;
return;
}
*info++ = item;
2009-01-06 06:53:34 +01:00
break;
case isc_info_sql_stmt_type:
switch (request->req_type)
{
case REQ_SELECT:
case REQ_EMBED_SELECT:
number = isc_info_sql_stmt_select;
break;
case REQ_SELECT_UPD:
number = isc_info_sql_stmt_select_for_upd;
break;
2008-02-28 14:48:16 +01:00
case REQ_CREATE_DB:
case REQ_DDL:
number = isc_info_sql_stmt_ddl;
break;
case REQ_GET_SEGMENT:
number = isc_info_sql_stmt_get_segment;
break;
case REQ_PUT_SEGMENT:
number = isc_info_sql_stmt_put_segment;
break;
case REQ_COMMIT:
case REQ_COMMIT_RETAIN:
number = isc_info_sql_stmt_commit;
break;
case REQ_ROLLBACK:
case REQ_ROLLBACK_RETAIN:
number = isc_info_sql_stmt_rollback;
break;
case REQ_START_TRANS:
number = isc_info_sql_stmt_start_trans;
break;
case REQ_INSERT:
number = isc_info_sql_stmt_insert;
break;
case REQ_UPDATE:
case REQ_UPDATE_CURSOR:
number = isc_info_sql_stmt_update;
break;
case REQ_DELETE:
case REQ_DELETE_CURSOR:
number = isc_info_sql_stmt_delete;
break;
case REQ_EXEC_PROCEDURE:
number = isc_info_sql_stmt_exec_procedure;
break;
case REQ_SET_GENERATOR:
number = isc_info_sql_stmt_set_generator;
break;
case REQ_SAVEPOINT:
number = isc_info_sql_stmt_savepoint;
break;
2008-12-05 02:20:14 +01:00
case REQ_EXEC_BLOCK:
number = isc_info_sql_stmt_exec_procedure;
break;
2008-12-05 02:20:14 +01:00
case REQ_SELECT_BLOCK:
number = isc_info_sql_stmt_select;
break;
default:
number = 0;
break;
}
length = convert((SLONG) number, buffer);
info = put_item(item, length, buffer, info, end_info);
if (!info) {
return;
}
2009-01-06 06:53:34 +01:00
break;
case isc_info_sql_sqlda_start:
length = *items++;
2009-01-06 06:53:34 +01:00
first_index = static_cast<USHORT>(gds__vax_integer(items, length));
items += length;
2009-01-06 06:53:34 +01:00
break;
case isc_info_sql_batch_fetch:
if (request->req_flags & REQ_no_batch)
number = 0;
else
number = 1;
length = convert((SLONG) number, buffer);
if (!(info = put_item(item, length, buffer, info, end_info))) {
return;
}
2009-01-06 06:53:34 +01:00
break;
case isc_info_sql_records:
2008-02-28 14:48:16 +01:00
length = get_request_info(tdbb, request, (SSHORT) sizeof(buffer), buffer);
2008-12-05 02:20:14 +01:00
if (length && !(info = put_item(item, length, buffer, info, end_info)))
{
return;
}
2009-01-06 06:53:34 +01:00
break;
case isc_info_sql_get_plan:
{
// be careful, get_plan_info() will reallocate the buffer to a
// larger size if it is not big enough
2009-01-06 06:53:34 +01:00
UCHAR* buffer_ptr = buffer;
length = get_plan_info(tdbb, request,
(SSHORT) sizeof(buffer), reinterpret_cast<SCHAR**>(&buffer_ptr));
2009-01-06 06:53:34 +01:00
if (length) {
info = put_item(item, length, buffer_ptr, info, end_info);
}
2009-01-06 06:53:34 +01:00
if (length > sizeof(buffer) || buffer_ptr != buffer) {
gds__free(buffer_ptr);
}
if (!info) {
return;
}
}
2009-01-06 06:53:34 +01:00
break;
case isc_info_sql_num_variables:
case isc_info_sql_describe_vars:
if (message)
{
number = (*message) ? (*message)->msg_index : 0;
length = convert((SLONG) number, buffer);
if (!(info = put_item(item, length, buffer, info, end_info))) {
return;
}
if (item == isc_info_sql_num_variables) {
continue;
}
2009-01-06 06:53:34 +01:00
const UCHAR* end_describe = items;
while (end_describe < end_items &&
*end_describe != isc_info_end && *end_describe != isc_info_sql_describe_end)
{
end_describe++;
}
2009-01-29 21:36:29 +01:00
info = var_info(*message, items, end_describe, info, end_info, first_index);
if (!info) {
2009-01-06 06:53:34 +01:00
return;
2009-01-29 21:36:29 +01:00
}
2009-01-06 06:53:34 +01:00
items = end_describe;
if (*items == isc_info_sql_describe_end) {
items++;
}
break;
}
2009-01-06 06:53:34 +01:00
// else fall into
default:
buffer[0] = item;
length = 1 + convert((SLONG) isc_infunk, buffer + 1);
2009-01-06 06:53:34 +01:00
if (!(info = put_item(isc_info_error, length, buffer, info, end_info))) {
return;
}
}
}
*info++ = isc_info_end;
if (start_info && (end_info - info >= 7))
{
SLONG number = info - start_info;
memmove(start_info + 7, start_info, number);
USHORT length = convert(number, buffer);
put_item(isc_info_length, length, buffer, start_info, end_info);
}
}
/**
2008-12-05 02:20:14 +01:00
var_info
2008-12-05 02:20:14 +01:00
@brief Provide information on an internal message.
2008-12-05 02:20:14 +01:00
@param message
@param items
@param end_describe
@param info
@param end
@param first_index
**/
2009-01-06 06:53:34 +01:00
static UCHAR* var_info(dsql_msg* message,
2003-10-16 10:51:06 +02:00
const UCHAR* items,
const UCHAR* const end_describe,
2008-12-05 02:20:14 +01:00
UCHAR* info,
const UCHAR* const end,
USHORT first_index)
2001-05-23 15:26:42 +02:00
{
if (!message || !message->msg_index)
return info;
2007-01-19 13:11:16 +01:00
Firebird::HalfStaticArray<const dsql_par*, 16> parameters;
2009-01-06 06:53:34 +01:00
for (const dsql_par* param = message->msg_parameters; param; param = param->par_next)
{
2007-01-19 13:11:16 +01:00
if (param->par_index)
{
if (param->par_index > parameters.getCount())
parameters.grow(param->par_index);
fb_assert(!parameters[param->par_index - 1]);
parameters[param->par_index - 1] = param;
}
}
2008-03-10 10:32:43 +01:00
UCHAR buf[128];
2007-04-11 18:05:40 +02:00
for (size_t i = 0; i < parameters.getCount(); i++)
2007-01-19 13:11:16 +01:00
{
const dsql_par* param = parameters[i];
fb_assert(param);
if (param->par_index >= first_index)
2005-03-31 08:21:55 +02:00
{
2008-03-10 10:32:43 +01:00
SLONG sql_len = param->par_desc.dsc_length;
SLONG sql_sub_type = 0;
SLONG sql_scale = 0;
SLONG sql_type = 0;
2008-12-05 02:20:14 +01:00
2005-05-22 05:11:41 +02:00
switch (param->par_desc.dsc_dtype)
{
2001-05-23 15:26:42 +02:00
case dtype_real:
sql_type = SQL_FLOAT;
break;
case dtype_array:
sql_type = SQL_ARRAY;
break;
case dtype_timestamp:
sql_type = SQL_TIMESTAMP;
break;
case dtype_sql_date:
sql_type = SQL_TYPE_DATE;
break;
case dtype_sql_time:
sql_type = SQL_TYPE_TIME;
break;
case dtype_double:
sql_type = SQL_DOUBLE;
2003-10-16 10:51:06 +02:00
sql_scale = param->par_desc.dsc_scale;
2001-05-23 15:26:42 +02:00
break;
case dtype_text:
2009-01-29 21:36:29 +01:00
sql_type = SQL_TEXT;
sql_sub_type = param->par_desc.dsc_sub_type;
2001-05-23 15:26:42 +02:00
break;
case dtype_blob:
sql_type = SQL_BLOB;
2003-10-16 10:51:06 +02:00
sql_sub_type = param->par_desc.dsc_sub_type;
2006-08-08 13:23:47 +02:00
sql_scale = param->par_desc.dsc_scale;
2001-05-23 15:26:42 +02:00
break;
case dtype_varying:
sql_type = SQL_VARYING;
sql_len -= sizeof(USHORT);
2003-10-16 10:51:06 +02:00
sql_sub_type = param->par_desc.dsc_sub_type;
2001-05-23 15:26:42 +02:00
break;
case dtype_short:
case dtype_long:
case dtype_int64:
2009-01-06 06:53:34 +01:00
switch (param->par_desc.dsc_dtype)
{
case dtype_short:
2001-05-23 15:26:42 +02:00
sql_type = SQL_SHORT;
2009-01-06 06:53:34 +01:00
break;
case dtype_long:
2001-05-23 15:26:42 +02:00
sql_type = SQL_LONG;
2009-01-06 06:53:34 +01:00
break;
default:
2001-05-23 15:26:42 +02:00
sql_type = SQL_INT64;
2009-01-06 06:53:34 +01:00
}
2003-10-16 10:51:06 +02:00
sql_scale = param->par_desc.dsc_scale;
if (param->par_desc.dsc_sub_type)
sql_sub_type = param->par_desc.dsc_sub_type;
2001-05-23 15:26:42 +02:00
break;
case dtype_quad:
sql_type = SQL_QUAD;
2003-10-16 10:51:06 +02:00
sql_scale = param->par_desc.dsc_scale;
2001-05-23 15:26:42 +02:00
break;
default:
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-804) <<
Arg::Gds(isc_dsql_datatype_err));
2001-05-23 15:26:42 +02:00
}
2003-10-16 10:51:06 +02:00
if (sql_type && (param->par_desc.dsc_flags & DSC_nullable))
2001-05-23 15:26:42 +02:00
sql_type++;
2005-05-22 05:11:41 +02:00
for (const UCHAR* describe = items; describe < end_describe;)
{
USHORT length;
const TEXT* name;
const UCHAR* buffer = buf;
UCHAR item = *describe++;
2009-01-06 06:53:34 +01:00
switch (item)
{
2003-11-08 00:27:24 +01:00
case isc_info_sql_sqlda_seq:
length = convert((SLONG) param->par_index, buf);
2001-05-23 15:26:42 +02:00
break;
2003-11-08 00:27:24 +01:00
case isc_info_sql_message_seq:
2001-05-23 15:26:42 +02:00
length = 0;
break;
2003-11-08 00:27:24 +01:00
case isc_info_sql_type:
length = convert((SLONG) sql_type, buf);
2001-05-23 15:26:42 +02:00
break;
2003-11-08 00:27:24 +01:00
case isc_info_sql_sub_type:
length = convert((SLONG) sql_sub_type, buf);
2001-05-23 15:26:42 +02:00
break;
2003-11-08 00:27:24 +01:00
case isc_info_sql_scale:
length = convert((SLONG) sql_scale, buf);
2001-05-23 15:26:42 +02:00
break;
2003-11-08 00:27:24 +01:00
case isc_info_sql_length:
length = convert((SLONG) sql_len, buf);
2001-05-23 15:26:42 +02:00
break;
2003-11-08 00:27:24 +01:00
case isc_info_sql_null_ind:
length = convert((SLONG) (sql_type & 1), buf);
2001-05-23 15:26:42 +02:00
break;
2003-11-08 00:27:24 +01:00
case isc_info_sql_field:
if (name = param->par_name) {
length = strlen(name);
buffer = reinterpret_cast<const UCHAR*>(name);
}
2001-05-23 15:26:42 +02:00
else
length = 0;
break;
2003-11-08 00:27:24 +01:00
case isc_info_sql_relation:
if (name = param->par_rel_name) {
length = strlen(name);
buffer = reinterpret_cast<const UCHAR*>(name);
}
2001-05-23 15:26:42 +02:00
else
length = 0;
break;
2003-11-08 00:27:24 +01:00
case isc_info_sql_owner:
if (name = param->par_owner_name) {
length = strlen(name);
buffer = reinterpret_cast<const UCHAR*>(name);
}
2001-05-23 15:26:42 +02:00
else
length = 0;
break;
case isc_info_sql_relation_alias:
if (name = param->par_rel_alias) {
length = strlen(name);
buffer = reinterpret_cast<const UCHAR*>(name);
}
else
length = 0;
break;
2003-11-08 00:27:24 +01:00
case isc_info_sql_alias:
if (name = param->par_alias) {
length = strlen(name);
buffer = reinterpret_cast<const UCHAR*>(name);
}
2001-05-23 15:26:42 +02:00
else
length = 0;
break;
default:
buf[0] = item;
2003-11-08 00:27:24 +01:00
item = isc_info_error;
length = 1 + convert((SLONG) isc_infunk, buf + 1);
2001-05-23 15:26:42 +02:00
break;
}
if (!(info = put_item(item, length, buffer, info, end)))
return info;
}
if (info + 1 >= end) {
2003-11-08 00:27:24 +01:00
*info = isc_info_truncated;
2001-05-23 15:26:42 +02:00
return NULL;
}
2003-11-08 00:27:24 +01:00
*info++ = isc_info_sql_describe_end;
} // if()
} // for()
2001-05-23 15:26:42 +02:00
return info;
}