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

3440 lines
84 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"
#include "../jrd/intlobj_new.h"
2008-02-28 14:48:16 +01:00
#include "../jrd/jrd.h"
#include "../jrd/CharSet.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"
#include "../jrd/ini_proto.h"
#include "../jrd/intl_proto.h"
2008-02-28 14:48:16 +01:00
#include "../jrd/jrd_proto.h"
#include "../jrd/tra_proto.h"
2009-02-01 23:10:12 +01:00
#include "../jrd/trace/TraceManager.h"
#include "../jrd/trace/TraceDSQLHelpers.h"
#include "../common/classes/init.h"
#include "../common/utils_proto.h"
#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*);
static void execute_immediate(thread_db*, Jrd::Attachment*, jrd_tra**,
2008-02-28 14:48:16 +01:00
USHORT, const TEXT*, USHORT,
2009-04-28 16:13:46 +02:00
USHORT, const UCHAR*, /*USHORT,*/ USHORT, const UCHAR*,
USHORT, UCHAR*, /*USHORT,*/ USHORT, UCHAR*, bool);
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(SLONG*, const UCHAR**, SLONG*, SCHAR**);
static USHORT get_request_info(thread_db*, dsql_req*, SLONG, UCHAR*);
static bool get_rsb_item(SLONG*, const UCHAR**, SLONG*, SCHAR**, USHORT*, USHORT*);
static dsql_dbb* init(Jrd::Attachment*);
2009-12-19 23:52:17 +01:00
static void map_in_out(dsql_req*, bool, const dsql_msg*, USHORT, const UCHAR*, USHORT, UCHAR*,
const UCHAR* = 0);
2009-12-19 23:52:17 +01:00
static USHORT parse_blr(dsql_req*, USHORT, const UCHAR*, const USHORT, const Array<dsql_par*>&);
static dsql_req* prepareRequest(thread_db*, dsql_dbb*, jrd_tra*, USHORT, const TEXT*, USHORT, USHORT, bool);
static dsql_req* prepareStatement(thread_db*, dsql_dbb*, jrd_tra*, USHORT, const TEXT*, USHORT, USHORT, bool);
static UCHAR* put_item(UCHAR, const USHORT, const UCHAR*, UCHAR*, const UCHAR* const, const bool copy = true);
static void release_statement(DsqlCompiledStatement* statement);
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*, ULONG, UCHAR*);
static UCHAR* var_info(const dsql_msg*, const UCHAR*, const UCHAR* const, UCHAR*,
const UCHAR* const, USHORT, bool);
2001-05-23 15:26:42 +02:00
static inline bool reqTypeWithCursor(DsqlCompiledStatement::Type type)
2009-02-01 23:10:12 +01:00
{
switch (type)
2009-02-01 23:10:12 +01:00
{
case DsqlCompiledStatement::TYPE_SELECT:
case DsqlCompiledStatement::TYPE_SELECT_BLOCK:
case DsqlCompiledStatement::TYPE_SELECT_UPD:
case DsqlCompiledStatement::TYPE_GET_SEGMENT:
case DsqlCompiledStatement::TYPE_PUT_SEGMENT:
2009-02-01 23:10:12 +01:00
return true;
}
return false;
}
#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
{
const UCHAR db_hdr_info_items[] =
2009-01-06 06:53:34 +01:00
{
2008-02-04 13:45:00 +01:00
isc_info_db_sql_dialect,
isc_info_ods_version,
isc_info_ods_minor_version,
isc_info_db_read_only,
isc_info_end
};
const UCHAR explain_info[] =
2009-01-06 06:53:34 +01:00
{
2008-02-04 13:45:00 +01:00
isc_info_access_path
};
const UCHAR record_info[] =
2009-01-06 06:53:34 +01:00
{
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
**/
dsql_req* DSQL_allocate_statement(thread_db* tdbb, Jrd::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();
DsqlCompiledStatement* statement = FB_NEW(pool) DsqlCompiledStatement(pool);
dsql_req* const request = FB_NEW(pool) dsql_req(statement);
2008-02-28 14:48:16 +01:00
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_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_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
2009-12-21 15:56:12 +01:00
Jrd::ContextPoolHolder context(tdbb, &request->getPool());
2008-02-01 21:18:11 +01:00
2009-12-21 15:56:12 +01:00
const DsqlCompiledStatement* statement = request->getStatement();
if (statement->getFlags() & DsqlCompiledStatement::FLAG_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
2009-12-21 15:56:12 +01:00
if ((SSHORT) in_msg_type == -1)
request->req_flags |= dsql_req::FLAG_EMBEDDED;
2001-05-23 15:26:42 +02:00
2009-04-18 16:13:26 +02:00
// Only allow NULL trans_handle if we're starting a transaction
2001-05-23 15:26:42 +02:00
if (!*tra_handle && statement->getType() != DsqlCompiledStatement::TYPE_START_TRANS)
2008-02-28 14:48:16 +01:00
{
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
2009-04-18 16:13:26 +02:00
// If the request is a SELECT or blob statement then this is an open.
// Make sure the cursor is not already open.
2001-05-23 15:26:42 +02:00
if (reqTypeWithCursor(statement->getType()))
2009-11-16 10:18:24 +01:00
{
if (request->req_flags & dsql_req::FLAG_OPENED_CURSOR)
{
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
2009-04-18 16:13:26 +02:00
// A select with a non zero output length is a singleton select
2008-02-28 14:48:16 +01:00
bool singleton;
if (statement->getType() == DsqlCompiledStatement::TYPE_SELECT && out_msg_length != 0)
2008-02-28 14:48:16 +01:00
singleton = true;
2009-12-21 15:56:12 +01:00
else
2008-02-28 14:48:16 +01:00
singleton = false;
2001-05-23 15:26:42 +02:00
2009-12-21 15:56:12 +01:00
if (!(request->req_flags & dsql_req::FLAG_EMBEDDED))
2008-02-28 14:48:16 +01:00
{
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
2009-12-25 20:29:58 +01:00
// If the output message length is zero on a TYPE_SELECT then we must
2009-04-18 16:13:26 +02:00
// be doing an OPEN cursor operation.
// If we do have an output message length, then we're doing
// a singleton SELECT. In that event, we don't add the cursor
// to the list of open cursors (it's not really open).
if (reqTypeWithCursor(statement->getType()) && !singleton)
{
request->req_flags |= dsql_req::FLAG_OPENED_CURSOR;
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_length
@param in_msg
@param out_blr_length
@param out_blr
@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,
Jrd::Attachment* attachment,
2008-02-28 14:48:16 +01:00
jrd_tra** tra_handle,
USHORT length, const TEXT* string, USHORT dialect,
USHORT in_blr_length, const UCHAR* in_blr,
USHORT in_msg_length, const UCHAR* in_msg,
2008-02-28 14:48:16 +01:00
USHORT out_blr_length, UCHAR* out_blr,
USHORT out_msg_length, UCHAR* out_msg,
bool isInternalRequest)
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_length, in_msg,
out_blr_length, out_blr, out_msg_length, out_msg,
isInternalRequest);
}
/**
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_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_length, UCHAR* dsql_msg_buf)
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
2009-12-21 15:56:12 +01:00
Jrd::ContextPoolHolder context(tdbb, &request->getPool());
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 & dsql_req::FLAG_OPENED_CURSOR))
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
dsql_msg* message = (dsql_msg*) request->getStatement()->getReceiveMsg();
2001-05-23 15:26:42 +02:00
2009-02-01 23:10:12 +01:00
// Set up things for tracing this call
Jrd::Attachment* att = request->req_dbb->dbb_attachment;
2009-02-01 23:10:12 +01:00
TraceDSQLFetch trace(att, request);
2009-04-18 16:13:26 +02:00
// Insure that the blr for the message is parsed, regardless of
// whether anything is found by the call to receive.
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
if (blr_length) {
2009-12-19 23:52:17 +01:00
parse_blr(request, blr_length, blr, msg_length, message->msg_parameters);
2008-02-28 14:48:16 +01:00
}
2001-05-23 15:26:42 +02:00
if (request->getStatement()->getType() == DsqlCompiledStatement::TYPE_GET_SEGMENT)
2008-02-28 14:48:16 +01:00
{
// For get segment, use the user buffer and indicator directly.
2008-02-01 21:18:11 +01:00
const dsql_par* parameter = request->getStatement()->getBlob()->blb_segment;
const dsql_par* null = parameter->par_null;
2008-02-01 21:18:11 +01:00
2009-12-19 23:52:17 +01:00
dsc userDesc;
if (!request->req_user_descs.get(parameter, userDesc))
userDesc.clear();
dsc userNullDesc;
if (!request->req_user_descs.get(null, userNullDesc))
userNullDesc.clear();
USHORT* ret_length = (USHORT *) (dsql_msg_buf + (IPTR) userNullDesc.dsc_address);
UCHAR* buffer = dsql_msg_buf + (IPTR) userDesc.dsc_address;
*ret_length = BLB_get_segment(tdbb, request->req_blb, buffer, userDesc.dsc_length);
2001-05-23 15:26:42 +02:00
if (request->req_blb->blb_flags & BLB_eof)
return 100;
2008-06-05 13:02:42 +02:00
if (request->req_blb->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
UCHAR* msgBuffer = request->req_msg_buffers[message->msg_buffer_number];
JRD_receive(tdbb, request->req_request, message->msg_number, message->msg_length,
msgBuffer, 0);
const dsql_par* const eof = request->getStatement()->getEof();
2009-02-01 23:10:12 +01:00
dsc eofDesc = eof->par_desc;
eofDesc.dsc_address = msgBuffer + IPTR(eofDesc.dsc_address);
const bool eof_reached = eof && !*((USHORT*) eofDesc.dsc_address);
2009-02-01 23:10:12 +01:00
if (eof_reached)
{
2009-02-01 23:10:12 +01:00
trace.fetch(true, res_successful);
return 100;
}
2001-05-23 15:26:42 +02:00
map_in_out(request, true, message, 0, blr, msg_length, dsql_msg_buf);
2008-02-28 14:48:16 +01:00
2009-02-01 23:10:12 +01:00
trace.fetch(false, res_successful);
2008-02-28 14:48:16 +01:00
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
2009-12-21 15:56:12 +01:00
Jrd::ContextPoolHolder context(tdbb, &request->getPool());
2001-05-23 15:26:42 +02:00
2009-11-16 10:18:24 +01:00
if (option & DSQL_drop)
{
2008-02-28 14:48:16 +01:00
// Release everything associated with the request
release_request(tdbb, request, true);
}
2009-11-16 10:18:24 +01:00
else if (option & DSQL_unprepare)
{
2008-02-28 14:48:16 +01:00
// Release everything but the request itself
release_request(tdbb, request, false);
}
2009-11-16 10:18:24 +01:00
else if (option & DSQL_close)
{
2008-02-28 14:48:16 +01:00
// Just close the cursor associated with the request
if (!(request->req_flags & dsql_req::FLAG_OPENED_CURSOR))
2008-02-28 14:48:16 +01:00
{
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_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,
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
2009-12-21 15:56:12 +01:00
Jrd::ContextPoolHolder context(tdbb, &request->getPool());
2008-02-01 21:18:11 +01:00
if (request->getStatement()->getFlags() & DsqlCompiledStatement::FLAG_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
2009-04-18 16:13:26 +02:00
// if the cursor isn't open, we've got a problem
2001-05-23 15:26:42 +02:00
if (request->getStatement()->getType() == DsqlCompiledStatement::TYPE_PUT_SEGMENT)
2008-02-28 14:48:16 +01:00
{
if (!(request->req_flags & dsql_req::FLAG_OPENED_CURSOR))
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
dsql_msg* message = (dsql_msg*) request->getStatement()->getReceiveMsg();
2001-05-23 15:26:42 +02:00
2009-04-18 16:13:26 +02:00
// Insure that the blr for the message is parsed, regardless of
// whether anything is found by the call to receive.
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
if (blr_length)
2009-12-19 23:52:17 +01:00
parse_blr(request, blr_length, blr, msg_length, message->msg_parameters);
2001-05-23 15:26:42 +02:00
if (request->getStatement()->getType() == DsqlCompiledStatement::TYPE_PUT_SEGMENT)
2009-11-16 10:18:24 +01:00
{
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
const dsql_par* parameter = request->getStatement()->getBlob()->blb_segment;
2008-02-01 21:18:11 +01:00
2009-12-19 23:52:17 +01:00
dsc userDesc;
if (!request->req_user_descs.get(parameter, userDesc))
userDesc.clear();
const UCHAR* buffer = dsql_msg_buf + (IPTR) userDesc.dsc_address;
BLB_put_segment(tdbb, request->req_blb, buffer, userDesc.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,
bool isInternalRequest)
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
2009-11-16 10:18:24 +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;
2009-11-16 10:18:24 +01:00
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
if (old_request && (old_request->req_flags & dsql_req::FLAG_OPENED_CURSOR))
2009-11-16 10:18:24 +01:00
{
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
2009-11-16 10:18:24 +01: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
2009-04-18 16:13:26 +02:00
// Figure out which parser version to use
// Since the API to dsql8_prepare is public and can not be changed, there needs to
// be a way to send the parser version to DSQL so that the parser can compare the keyword
// version to the parser version. To accomplish this, the parser version is combined with
// the client dialect and sent across that way. In dsql8_prepare_statement, the parser version
// and client dialect are separated and passed on to their final destinations. The information
// is combined as follows:
// Dialect * 10 + parser_version
//
// and is extracted in dsql8_prepare_statement as follows:
// parser_version = ((dialect *10)+parser_version)%10
// client_dialect = ((dialect *10)+parser_version)/10
//
// For example, parser_version = 1 and client dialect = 1
//
// combined = (1 * 10) + 1 == 11
//
// parser = (combined) %10 == 1
// dialect = (combined) / 19 == 1
//
// If the parser version is not part of the dialect, then assume that the
// connection being made is a local classic connection.
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
USHORT parser_version;
if ((dialect / 10) == 0)
parser_version = 2;
2009-11-16 10:18:24 +01:00
else
{
2008-02-28 14:48:16 +01:00
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
request = prepareRequest(tdbb, database, transaction, length, string, dialect, parser_version,
isInternalRequest);
2008-03-01 16:08:11 +01:00
// Can not prepare a CREATE DATABASE/SCHEMA statement
if (request->getStatement()->getType() == DsqlCompiledStatement::TYPE_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-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
{
2009-12-21 15:56:12 +01:00
Jrd::ContextPoolHolder context(tdbb, &old_request->getPool());
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
2009-12-21 15:56:12 +01:00
Jrd::ContextPoolHolder context(tdbb, &request->getPool());
2008-02-28 14:48:16 +01:00
sql_info(tdbb, request, item_length, items, buffer_length, buffer);
}
catch (const Firebird::Exception&)
{
if (request)
{
2009-12-21 15:56:12 +01:00
Jrd::ContextPoolHolder context(tdbb, &request->getPool());
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
2009-12-19 16:39:23 +01:00
DSQL_set_cursor
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
2009-10-31 09:20:14 +01:00
@param tdbb
@param req_handle
2008-02-28 14:48:16 +01:00
@param input_cursor
**/
2009-11-16 10:18:24 +01:00
void DSQL_set_cursor(thread_db* tdbb, dsql_req* request, const TEXT* input_cursor)
2008-02-28 14:48:16 +01:00
{
SET_TDBB(tdbb);
2001-05-23 15:26:42 +02:00
2009-12-21 15:56:12 +01:00
Jrd::ContextPoolHolder context(tdbb, &request->getPool());
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());
2009-11-16 10:18:24 +01:00
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
}
2009-11-16 10:18:24 +01:00
else
{
2008-02-28 14:48:16 +01:00
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,
ULONG 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
2009-12-21 15:56:12 +01:00
Jrd::ContextPoolHolder context(tdbb, &request->getPool());
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
Jrd::Attachment* attachment = request->req_dbb->dbb_attachment;
2008-02-28 14:48:16 +01:00
if (request->req_request)
{
ThreadStatusGuard status_vector(tdbb);
try
{
if (request->getStatement()->getType() == DsqlCompiledStatement::TYPE_GET_SEGMENT ||
request->getStatement()->getType() == DsqlCompiledStatement::TYPE_PUT_SEGMENT)
{
BLB_close(tdbb, request->req_blb);
request->req_blb = NULL;
}
else
2009-02-01 23:10:12 +01:00
{
// Report some remaining fetches if any
if (request->req_fetch_baseline)
{
TraceDSQLFetch trace(attachment, request);
trace.fetch(true, res_successful);
}
if (request->req_traced && TraceManager::need_dsql_free(attachment))
{
TraceSQLStatementImpl stmt(request, NULL);
TraceManager::event_dsql_free(attachment, &stmt, DSQL_close);
}
JRD_unwind_request(tdbb, request->req_request, 0);
2009-02-01 23:10:12 +01:00
}
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 &= ~dsql_req::FLAG_OPENED_CURSOR;
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
const dsql_blb* blob = request->getStatement()->getBlob();
map_in_out(request, false, 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);
2009-11-16 10:18:24 +01:00
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);
2009-11-16 10:18:24 +01:00
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
UCHAR* msgBuffer = request->req_msg_buffers[parameter->par_message->msg_buffer_number];
dsc desc = parameter->par_desc;
desc.dsc_address = msgBuffer + IPTR(desc.dsc_address);
bid* blob_id = (bid*) desc.dsc_address;
if (request->getStatement()->getType() == DsqlCompiledStatement::TYPE_GET_SEGMENT)
2001-12-24 03:51:06 +01:00
{
if (null)
{
desc = null->par_desc;
desc.dsc_address = msgBuffer + IPTR(desc.dsc_address);
if (*((SSHORT*) desc.dsc_address) < 0)
memset(blob_id, 0, sizeof(bid));
2001-12-24 03:51:06 +01:00
}
2008-02-01 21:18:11 +01:00
request->req_blb = 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
memset(blob_id, 0, sizeof(bid));
2008-02-01 21:18:11 +01:00
request->req_blb = BLB_create2(tdbb, request->req_transaction, blob_id, bpb_length, bpb);
2008-02-01 21:18:11 +01:00
map_in_out(request, true, 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_length
@param in_msg
@param out_blr_length
@param out_blr
@param out_msg_length
@param out_msg
**/
static void execute_immediate(thread_db* tdbb,
Jrd::Attachment* attachment,
2008-02-28 14:48:16 +01:00
jrd_tra** tra_handle,
USHORT length, const TEXT* string, USHORT dialect,
USHORT in_blr_length, const UCHAR* in_blr,
USHORT in_msg_length, const UCHAR* in_msg,
2008-02-28 14:48:16 +01:00
USHORT out_blr_length, UCHAR* out_blr,
USHORT out_msg_length, UCHAR* out_msg,
bool isInternalRequest)
2008-02-28 14:48:16 +01:00
{
SET_TDBB(tdbb);
2009-11-16 10:18:24 +01: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 (!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 {
2009-04-18 16:13:26 +02:00
// Figure out which parser version to use
// 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.
2008-02-28 14:48:16 +01:00
USHORT parser_version;
if ((dialect / 10) == 0)
parser_version = 2;
2009-11-16 10:18:24 +01:00
else
{
2008-02-28 14:48:16 +01:00
parser_version = dialect % 10;
dialect /= 10;
}
request = prepareRequest(tdbb, database, *tra_handle, length, string, dialect, parser_version,
isInternalRequest);
2008-02-28 14:48:16 +01:00
2009-12-21 15:56:12 +01:00
Jrd::ContextPoolHolder context(tdbb, &request->getPool());
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)
{
2009-12-21 15:56:12 +01:00
Jrd::ContextPoolHolder context(tdbb, &request->getPool());
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;
2001-05-23 15:26:42 +02:00
switch (request->getStatement()->getType())
2008-02-01 21:18:11 +01:00
{
case DsqlCompiledStatement::TYPE_START_TRANS:
2009-01-06 06:53:34 +01:00
JRD_start_transaction(tdbb, &request->req_transaction, 1, &request->req_dbb->dbb_attachment,
request->getStatement()->getBlrData().getCount(),
request->getStatement()->getBlrData().begin());
*tra_handle = request->req_transaction;
return;
2001-05-23 15:26:42 +02:00
case DsqlCompiledStatement::TYPE_COMMIT:
JRD_commit_transaction(tdbb, &request->req_transaction);
*tra_handle = NULL;
return;
2001-05-23 15:26:42 +02:00
case DsqlCompiledStatement::TYPE_COMMIT_RETAIN:
JRD_commit_retaining(tdbb, &request->req_transaction);
return;
2001-05-23 15:26:42 +02:00
case DsqlCompiledStatement::TYPE_ROLLBACK:
JRD_rollback_transaction(tdbb, &request->req_transaction);
*tra_handle = NULL;
return;
2001-05-23 15:26:42 +02:00
case DsqlCompiledStatement::TYPE_ROLLBACK_RETAIN:
JRD_rollback_retaining(tdbb, &request->req_transaction);
return;
case DsqlCompiledStatement::TYPE_CREATE_DB:
case DsqlCompiledStatement::TYPE_DDL:
2009-11-16 10:18:24 +01:00
{
TraceDSQLExecute trace(request->req_dbb->dbb_attachment, request);
DDL_execute(request);
trace.finish(false, res_successful);
return;
}
2001-05-23 15:26:42 +02:00
case DsqlCompiledStatement::TYPE_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 DsqlCompiledStatement::TYPE_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 DsqlCompiledStatement::TYPE_SELECT:
case DsqlCompiledStatement::TYPE_SELECT_UPD:
case DsqlCompiledStatement::TYPE_INSERT:
case DsqlCompiledStatement::TYPE_UPDATE:
case DsqlCompiledStatement::TYPE_UPDATE_CURSOR:
case DsqlCompiledStatement::TYPE_DELETE:
case DsqlCompiledStatement::TYPE_DELETE_CURSOR:
case DsqlCompiledStatement::TYPE_EXEC_PROCEDURE:
case DsqlCompiledStatement::TYPE_SET_GENERATOR:
case DsqlCompiledStatement::TYPE_SAVEPOINT:
case DsqlCompiledStatement::TYPE_EXEC_BLOCK:
case DsqlCompiledStatement::TYPE_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
const dsql_msg* message = request->getStatement()->getSendMsg();
2009-02-02 04:35:52 +01:00
if (message)
map_in_out(request, false, message, in_blr_length, in_blr, in_msg_length, NULL, in_msg);
2008-02-28 14:48:16 +01:00
2009-02-01 23:10:12 +01:00
// we need to map_in_out before tracing of execution start to let trace
// manager know statement parameters values
TraceDSQLExecute trace(request->req_dbb->dbb_attachment, request);
2009-02-02 04:35:52 +01:00
if (!message)
2009-02-01 23:10:12 +01:00
JRD_start(tdbb, request->req_request, request->req_transaction, 0);
2009-02-02 04:35:52 +01:00
else
{
UCHAR* msgBuffer = request->req_msg_buffers[message->msg_buffer_number];
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, msgBuffer, 0);
2001-05-23 15:26:42 +02:00
}
2009-12-25 20:29:58 +01:00
// TYPE_EXEC_BLOCK has no outputs so there are no out_msg
// supplied from client side, but TYPE_EXEC_BLOCK requires
// 2-byte message for EOS synchronization
const bool isBlock = (request->getStatement()->getType() == DsqlCompiledStatement::TYPE_EXEC_BLOCK);
message = request->getStatement()->getReceiveMsg();
2008-02-28 14:48:16 +01:00
if ((out_msg_length && message) || isBlock)
{
UCHAR temp_buffer[FB_DOUBLE_ALIGN * 2];
dsql_msg temp_msg(*getDefaultMemoryPool());
2009-04-18 16:13:26 +02:00
// Insure that the blr for the message is parsed, regardless of
// whether anything is found by the call to receive.
2001-05-23 15:26:42 +02:00
UCHAR* msgBuffer = request->req_msg_buffers[message->msg_buffer_number];
if (out_msg_length && out_blr_length) {
2009-12-19 23:52:17 +01:00
parse_blr(request, out_blr_length, out_blr, out_msg_length, message->msg_parameters);
2008-12-05 02:20:14 +01:00
}
2009-11-16 10:18:24 +01:00
else if (!out_msg_length && isBlock)
{
message = &temp_msg;
temp_msg.msg_number = 1;
temp_msg.msg_length = 2;
msgBuffer = (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,
msgBuffer, 0);
2001-05-23 15:26:42 +02:00
if (out_msg_length)
map_in_out(request, true, 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;
2009-04-18 16:13:26 +02:00
// 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.
2001-05-23 15:26:42 +02:00
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;
ISC_STATUS_ARRAY localStatus;
2008-02-01 21:18:11 +01:00
for (counter = 0; counter < 2 && !status; counter++)
{
AutoSetRestore<ISC_STATUS*> autoStatus(&tdbb->tdbb_status_vector, localStatus);
fb_utils::init_status(localStatus);
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
2009-04-18 16:13:26 +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
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
if (!status)
ERRD_post(Arg::Gds(isc_sing_select_err));
2008-02-28 14:48:16 +01:00
else if (status == isc_req_sync && counter == 1)
ERRD_post(Arg::Gds(isc_stream_eof));
2008-02-28 14:48:16 +01:00
else if (status != isc_req_sync)
status_exception::raise(localStatus);
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->getStatement()->getType() == DsqlCompiledStatement::TYPE_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->getStatement()->getType() == DsqlCompiledStatement::TYPE_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
}
}
2009-02-01 23:10:12 +01:00
const bool have_cursor = reqTypeWithCursor(request->getStatement()->getType()) && !singleton;
2009-02-01 23:10:12 +01:00
trace.finish(have_cursor, res_successful);
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 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;
dsc desc;
UCHAR* msgBuffer = request->req_msg_buffers[parameter->par_message->msg_buffer_number];
if (null)
2008-03-02 09:40:09 +01:00
{
desc = null->par_desc;
desc.dsc_address = msgBuffer + IPTR(null->par_desc.dsc_address);
if (*((SSHORT*) desc.dsc_address))
2001-05-23 15:26:42 +02:00
return 0;
2008-03-02 09:40:09 +01:00
}
2001-05-23 15:26:42 +02:00
desc = parameter->par_desc;
desc.dsc_address = msgBuffer + IPTR(parameter->par_desc.dsc_address);
return *((SSHORT*) desc.dsc_address);
2001-05-23 15:26:42 +02:00
}
/**
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
**/
static bool get_indices(SLONG* explain_length_ptr, const UCHAR** explain_ptr,
SLONG* plan_length_ptr, SCHAR** plan_ptr)
2001-05-23 15:26:42 +02:00
{
USHORT length;
SLONG explain_length = *explain_length_ptr;
const UCHAR* explain = *explain_ptr;
SLONG& plan_length = *plan_length_ptr;
SCHAR*& plan = *plan_ptr;
2001-05-23 15:26:42 +02:00
2009-04-18 16:13:26 +02:00
// go through the index tree information, just
// extracting the indices used
2001-05-23 15:26:42 +02:00
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
2009-11-16 10:18:24 +01: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;
2001-05-23 15:26:42 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
/**
2008-12-05 02:20:14 +01:00
2009-02-01 23:10:12 +01:00
DSQL_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 out_buffer
@param realloc
**/
ULONG DSQL_get_plan_info(thread_db* tdbb,
2009-02-09 16:25:01 +01:00
const dsql_req* request,
SLONG buffer_length,
SCHAR** out_buffer,
const bool realloc)
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<UCHAR, BUFFER_LARGE> explain_buffer;
explain_buffer.resize(BUFFER_LARGE);
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_USHORT);
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* buffer_ptr = *out_buffer;
SCHAR* plan;
for (int i = 0; i < 2; i++)
{
const UCHAR* 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
SLONG explain_length = (ULONG) *explain++;
explain_length += (ULONG) (*explain++) << 8;
2001-05-23 15:26:42 +02:00
plan = buffer_ptr;
2009-04-18 16:13:26 +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.
2004-10-07 10:27:45 +02:00
USHORT join_count = 0, level = 0;
2002-06-29 08:56:51 +02:00
const ULONG full_len = buffer_length;
memset(plan, 0, full_len);
// This is testing code for the limit case,
// please do not enable for normal operations.
/*
if (full_len == ULONG(MAX_USHORT) - 4)
{
const size_t test_offset = 55000;
memset(plan, '.', test_offset);
plan += test_offset;
buffer_length -= test_offset;
}
*/
2001-05-23 15:26:42 +02:00
// keep going until we reach the end of the explain info
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 ||
2009-06-19 04:29:08 +02:00
(!realloc && full_len == ULONG(MAX_USHORT) - 4))
{
2009-06-18 18:53:00 +02:00
const ptrdiff_t diff = buffer_ptr + full_len - plan;
if (diff < 3) {
plan -= 3 - diff;
}
fb_assert(plan > buffer_ptr);
*plan++ = '.';
*plan++ = '.';
*plan++ = '.';
if (!realloc)
return plan - buffer_ptr;
++i;
break;
}
if (!realloc)
return full_len - buffer_length;
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_USHORT;
2008-10-16 10:51:51 +02:00
char* const temp = static_cast<char*>(gds__alloc(new_length));
2009-11-16 10:18:24 +01:00
if (!temp)
{
// NOMEM. Do not attempt one more try
i++;
break;
}
2009-01-06 06:53:34 +01:00
buffer_ptr = temp;
buffer_length = (SLONG) 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
**/
2009-11-16 10:18:24 +01:00
static USHORT get_request_info(thread_db* tdbb, dsql_req* request, SLONG 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, 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
**/
static bool get_rsb_item(SLONG* explain_length_ptr,
const UCHAR** explain_ptr,
SLONG* plan_length_ptr,
2001-12-24 03:51:06 +01:00
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
SLONG explain_length = *explain_length_ptr;
const UCHAR* explain = *explain_ptr;
SLONG& 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:
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)--;
}
2009-04-18 16:13:26 +02:00
// 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
2009-04-18 16:13:26 +02:00
// for the single relation case, initiate
// the relation with a parenthesis
2001-05-23 15:26:42 +02:00
2009-11-16 10:18:24 +01:00
if (!*parent_join_count)
{
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
// if this isn't the first relation, put out a comma
2001-05-23 15:26:42 +02:00
2009-11-16 10:18:24 +01: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--;
SSHORT length = (USHORT) *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--;
2009-04-18 16:13:26 +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
switch (rsb_type = *explain++)
{
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--;
fb_assert(*explain > 0U);
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 = union_count ? 0 : 1;
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;
}
2009-04-18 16:13:26 +02:00
// 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:
case isc_info_rsb_hash:
2001-05-23 15:26:42 +02:00
2009-04-18 16:13:26 +02:00
// if this join is itself part of a join list,
// but not the first item, then 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
}
2008-12-05 02:20:14 +01:00
// put out the join type
2001-05-23 15:26:42 +02:00
if (rsb_type == isc_info_rsb_merge)
p = "MERGE (";
else if (rsb_type == isc_info_rsb_hash)
p = "HASH (";
else
2004-10-07 10:27:45 +02:00
p = "JOIN (";
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
2009-04-18 16:13:26 +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
// in the union, so the sort doesn't really apply to a particular plan
2001-05-23 15:26:42 +02:00
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++;
2009-04-18 16:13:26 +02:00
// the rsb_sort should always be followed by a begin...end block,
// allowing us to include everything inside the sort in parentheses
2001-05-23 15:26:42 +02:00
{ // 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;
2001-05-23 15:26:42 +02:00
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
**/
static dsql_dbb* init(Jrd::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;
2009-12-27 17:49:46 +01:00
INI_init_dsql(tdbb, database);
UCHAR buffer[BUFFER_TINY];
2008-02-28 14:48:16 +01:00
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
}
const UCHAR* data = buffer;
2008-02-28 14:48:16 +01:00
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:
2009-07-06 12:43:47 +02:00
fb_assert(l == 1);
2008-02-28 14:48:16 +01:00
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 < 12)
2008-02-28 14:48:16 +01:00
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-804) <<
Arg::Gds(isc_dsql_too_old_ods) << Arg::Num(12));
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
case isc_info_db_read_only:
2009-07-06 12:43:47 +02:00
fb_assert(l == 1);
2008-02-28 14:48:16 +01:00
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
**/
2009-12-19 23:52:17 +01:00
static void map_in_out(dsql_req* request, bool toExternal, const dsql_msg* message,
USHORT blr_length, const UCHAR* blr, USHORT msg_length, UCHAR* dsql_msg_buf,
const UCHAR* in_dsql_msg_buf)
2001-05-23 15:26:42 +02:00
{
2009-12-19 23:52:17 +01:00
USHORT count = parse_blr(request, blr_length, blr, msg_length, message->msg_parameters);
2001-05-23 15:26:42 +02:00
bool err = false;
2001-05-23 15:26:42 +02:00
for (size_t i = 0; i < message->msg_parameters.getCount(); ++i)
{
dsql_par* parameter = message->msg_parameters[i];
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-12-19 23:52:17 +01:00
dsc desc;
if (!request->req_user_descs.get(parameter, desc))
desc.clear();
USHORT length = (IPTR) desc.dsc_address + desc.dsc_length;
if (length > msg_length || !desc.dsc_dtype)
{
err = true;
break;
}
2001-05-23 15:26:42 +02:00
UCHAR* msgBuffer = request->req_msg_buffers[parameter->par_message->msg_buffer_number];
SSHORT* flag = NULL;
dsql_par* const null_ind = parameter->par_null;
if (null_ind != NULL)
{
2009-12-19 23:52:17 +01:00
dsc userNullDesc;
if (!request->req_user_descs.get(null_ind, userNullDesc))
userNullDesc.clear();
const USHORT null_offset = (IPTR) userNullDesc.dsc_address;
2001-05-23 15:26:42 +02:00
length = null_offset + sizeof(SSHORT);
if (length > msg_length)
{
err = true;
2001-05-23 15:26:42 +02:00
break;
}
2001-05-23 15:26:42 +02:00
dsc nullDesc = null_ind->par_desc;
nullDesc.dsc_address = msgBuffer + IPTR(nullDesc.dsc_address);
if (toExternal)
2009-11-16 10:18:24 +01:00
{
flag = reinterpret_cast<SSHORT*>(dsql_msg_buf + null_offset);
*flag = *reinterpret_cast<const SSHORT*>(nullDesc.dsc_address);
2001-05-23 15:26:42 +02:00
}
2009-11-16 10:18:24 +01:00
else
{
flag = reinterpret_cast<SSHORT*>(nullDesc.dsc_address);
*flag = *reinterpret_cast<const SSHORT*>(in_dsql_msg_buf + null_offset);
2001-05-23 15:26:42 +02:00
}
}
dsc parDesc = parameter->par_desc;
parDesc.dsc_address = msgBuffer + IPTR(parDesc.dsc_address);
if (toExternal)
{
desc.dsc_address = dsql_msg_buf + (IPTR) desc.dsc_address;
if (!flag || *flag >= 0)
{
MOVD_move(&parDesc, &desc);
}
else
{
memset(desc.dsc_address, 0, desc.dsc_length);
}
}
2001-05-23 15:26:42 +02:00
else if (!flag || *flag >= 0)
{
if (!(parDesc.dsc_flags & DSC_null))
{
// 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, &parDesc);
}
}
else
{
memset(parDesc.dsc_address, 0, parDesc.dsc_length);
}
2001-05-23 15:26:42 +02:00
count--;
}
}
2001-05-23 15:26:42 +02:00
2009-04-18 16:13:26 +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.
2001-05-23 15:26:42 +02:00
if (err || count)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-804) <<
Arg::Gds(isc_dsql_sqlda_err));
}
2001-05-23 15:26:42 +02:00
2009-12-21 15:56:12 +01:00
const DsqlCompiledStatement* statement = request->getStatement();
const dsql_par* parameter;
const dsql_par* dbkey;
if (!toExternal && (dbkey = statement->getParentDbKey()) &&
(parameter = statement->getDbKey()))
{
UCHAR* parentMsgBuffer = statement->getParentRequest() ?
statement->getParentRequest()->req_msg_buffers[dbkey->par_message->msg_buffer_number] : NULL;
UCHAR* msgBuffer = request->req_msg_buffers[parameter->par_message->msg_buffer_number];
fb_assert(parentMsgBuffer);
dsc parentDesc = dbkey->par_desc;
parentDesc.dsc_address = parentMsgBuffer + IPTR(parentDesc.dsc_address);
dsc desc = parameter->par_desc;
desc.dsc_address = msgBuffer + IPTR(desc.dsc_address);
MOVD_move(&parentDesc, &desc);
dsql_par* null_ind = parameter->par_null;
if (null_ind != NULL)
{
desc = null_ind->par_desc;
desc.dsc_address = msgBuffer + IPTR(desc.dsc_address);
SSHORT* flag = (SSHORT*) desc.dsc_address;
2001-05-23 15:26:42 +02:00
*flag = 0;
}
}
const dsql_par* rec_version;
if (!toExternal && (rec_version = statement->getParentRecVersion()) &&
(parameter = statement->getRecVersion()))
{
UCHAR* parentMsgBuffer = statement->getParentRequest() ?
statement->getParentRequest()->req_msg_buffers[rec_version->par_message->msg_buffer_number] :
NULL;
UCHAR* msgBuffer = request->req_msg_buffers[parameter->par_message->msg_buffer_number];
fb_assert(parentMsgBuffer);
dsc parentDesc = rec_version->par_desc;
parentDesc.dsc_address = parentMsgBuffer + IPTR(parentDesc.dsc_address);
dsc desc = parameter->par_desc;
desc.dsc_address = msgBuffer + IPTR(desc.dsc_address);
MOVD_move(&parentDesc, &desc);
dsql_par* null_ind = parameter->par_null;
if (null_ind != NULL)
{
desc = null_ind->par_desc;
desc.dsc_address = msgBuffer + IPTR(desc.dsc_address);
SSHORT* flag = (SSHORT*) 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-12-19 23:52:17 +01:00
static USHORT parse_blr(dsql_req* request, USHORT blr_length, const UCHAR* blr,
const USHORT msg_length, const Array<dsql_par*>& parameters_list)
2001-05-23 15:26:42 +02:00
{
2009-12-19 23:52:17 +01:00
HalfStaticArray<const dsql_par*, 16> parameters;
for (size_t i = 0; i < parameters_list.getCount(); ++i)
{
dsql_par* param = parameters_list[i];
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;
}
}
2009-04-18 16:13:26 +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.
2001-05-23 15:26:42 +02:00
if (!blr_length)
{
return parameters.getCount();
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);
2009-12-19 23:52:17 +01:00
const dsql_par* const parameter = parameters[index - 1];
fb_assert(parameter);
// ASF: Older than 2.5 engine hasn't validating strings in DSQL. After this has been
// implemented in 2.5, selecting a NONE column with UTF-8 attachment charset started
// failing. The real problem is that the client encodes SQL_TEXT/SQL_VARYING using
// blr_text/blr_varying (i.e. with the connection charset). I'm reseting the charset
// here at the server as a way to make older (and not yet changed) client work
// correctly.
2009-12-19 23:52:17 +01:00
if (desc.isText())
desc.setTextType(ttype_none);
request->req_user_descs.put(parameter, desc);
dsql_par* null = parameter->par_null;
if (null)
2003-09-13 14:16:48 +02:00
{
2009-12-19 23:52:17 +01:00
desc.clear();
desc.dsc_dtype = dtype_short;
desc.dsc_scale = 0;
desc.dsc_length = sizeof(SSHORT);
desc.dsc_address = (UCHAR*)(IPTR) null_offset;
request->req_user_descs.put(null, desc);
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;
}
// Prepare a request for execution. Return SQL status code.
// Note: caller is responsible for pool handling.
static dsql_req* prepareRequest(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction,
USHORT stringLength, const TEXT* string, USHORT clientDialect, USHORT parserVersion,
bool isInternalRequest)
{
return prepareStatement(tdbb, database, transaction, stringLength, string, clientDialect,
parserVersion, isInternalRequest);
}
// Prepare a statement for execution. Return SQL status code.
// Note: caller is responsible for pool handling.
static dsql_req* prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction,
USHORT string_length, const TEXT* string, USHORT client_dialect, USHORT parser_version,
bool isInternalRequest)
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
TraceDSQLPrepare trace(database->dbb_attachment, string_length, string);
2009-02-01 23:10:12 +01:00
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
2009-11-16 10:18:24 +01: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
2009-04-18 16:13:26 +02:00
// Get rid of the trailing ";" if there is one.
2001-05-23 15:26:42 +02:00
for (const TEXT* p = string + string_length; p-- > string;)
{
2009-11-16 10:18:24 +01:00
if (*p != ' ')
{
2001-05-23 15:26:42 +02:00
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();
2009-02-01 23:10:12 +01:00
DsqlCompiledStatement* statement = FB_NEW(pool) DsqlCompiledStatement(pool);
DsqlCompilerScratch* scratch = FB_NEW(pool) DsqlCompilerScratch(pool, database,
transaction, statement);
scratch->clientDialect = client_dialect;
dsql_req* request = FB_NEW(statement->getPool()) dsql_req(statement);
request->req_dbb = database;
request->req_transaction = transaction;
request->req_traced = true;
trace.setStatement(request);
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,
scratch->getAttachment()->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();
Firebird::string transformedText = parser.getTransformedString();
SSHORT charSetId = database->dbb_attachment->att_charset;
2001-05-23 15:26:42 +02:00
// If the attachment charset is NONE, replace non-ASCII characters by question marks, so that
// engine internals doesn't receive non-mappeable data to UTF8. If an attachment charset is
// used, validate the string.
if (charSetId == CS_NONE)
{
for (char* p = transformedText.begin(), *end = transformedText.end(); p < end; ++p)
{
if (UCHAR(*p) > 0x7F)
*p = '?';
}
}
else
{
CharSet* charSet = INTL_charset_lookup(tdbb, charSetId);
if (!charSet->wellFormed(transformedText.length(), (const UCHAR*) transformedText.begin(), NULL))
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_malformed_string));
}
}
statement->setSqlText(FB_NEW(pool) RefString(pool, transformedText));
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->setSendMsg(FB_NEW(pool) dsql_msg(pool));
dsql_msg* message = FB_NEW(pool) dsql_msg(pool);
statement->setReceiveMsg(message);
2001-05-23 15:26:42 +02:00
message->msg_number = 1;
statement->setType(DsqlCompiledStatement::TYPE_SELECT);
2001-05-23 15:26:42 +02:00
2009-04-18 16:13:26 +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(scratch, node);
2001-05-23 15:26:42 +02:00
if (!node)
return request;
2001-05-23 15:26:42 +02:00
switch (statement->getType())
{
case DsqlCompiledStatement::TYPE_COMMIT:
case DsqlCompiledStatement::TYPE_COMMIT_RETAIN:
case DsqlCompiledStatement::TYPE_ROLLBACK:
case DsqlCompiledStatement::TYPE_ROLLBACK_RETAIN:
case DsqlCompiledStatement::TYPE_GET_SEGMENT:
case DsqlCompiledStatement::TYPE_PUT_SEGMENT:
case DsqlCompiledStatement::TYPE_START_TRANS:
request->req_traced = false;
break;
default:
request->req_traced = true;
}
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->getType() == DsqlCompiledStatement::TYPE_DDL && parser.isStmtAmbiguous() &&
scratch->getAttachment()->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) <<
2009-12-24 11:42:32 +01:00
Arg::Num(scratch->getAttachment()->dbb_db_SQL_dialect));
}
2001-05-23 15:26:42 +02:00
switch (statement->getType())
{
// Work on blob segment statements
case DsqlCompiledStatement::TYPE_GET_SEGMENT:
case DsqlCompiledStatement::TYPE_PUT_SEGMENT:
GEN_port(scratch, statement->getBlob()->blb_open_in_msg);
GEN_port(scratch, statement->getBlob()->blb_open_out_msg);
GEN_port(scratch, statement->getBlob()->blb_segment_msg);
break;
}
2001-05-23 15:26:42 +02:00
switch (statement->getType())
{
case DsqlCompiledStatement::TYPE_COMMIT:
case DsqlCompiledStatement::TYPE_COMMIT_RETAIN:
case DsqlCompiledStatement::TYPE_ROLLBACK:
case DsqlCompiledStatement::TYPE_ROLLBACK_RETAIN:
return request;
case DsqlCompiledStatement::TYPE_START_TRANS:
GEN_start_transaction(scratch, node);
return request;
default:
if (client_dialect > SQL_DIALECT_V5)
2009-12-24 13:56:31 +01:00
statement->addFlags(DsqlCompiledStatement::FLAG_BLR_VERSION5);
else
2009-12-24 13:56:31 +01:00
statement->addFlags(DsqlCompiledStatement::FLAG_BLR_VERSION4);
GEN_request(request, scratch, node);
// fall into
case DsqlCompiledStatement::TYPE_GET_SEGMENT:
case DsqlCompiledStatement::TYPE_PUT_SEGMENT:
{
// Create the messages buffers
for (size_t i = 0; i < scratch->ports.getCount(); ++i)
{
dsql_msg* message = scratch->ports[i];
// Allocate buffer for message
const ULONG newLen = message->msg_length + FB_DOUBLE_ALIGN - 1;
message->msg_buffer_number = request->req_msg_buffers.getCount();
request->req_msg_buffers.grow(message->msg_buffer_number + 1);
UCHAR*& msgBuffer = request->req_msg_buffers[message->msg_buffer_number];
fb_assert(!msgBuffer);
msgBuffer = FB_NEW(*tdbb->getDefaultPool()) UCHAR[newLen];
msgBuffer = (UCHAR*) FB_ALIGN((U_IPTR) msgBuffer, FB_DOUBLE_ALIGN);
}
break;
}
2001-05-23 15:26:42 +02:00
}
switch (statement->getType())
{
case DsqlCompiledStatement::TYPE_GET_SEGMENT:
case DsqlCompiledStatement::TYPE_PUT_SEGMENT:
return request;
}
2005-05-22 05:11:41 +02:00
const ULONG length = (ULONG) statement->getBlrData().getCount();
2008-12-05 02:20:14 +01:00
// stop here for ddl statements
2001-05-23 15:26:42 +02:00
if (statement->getType() == DsqlCompiledStatement::TYPE_CREATE_DB ||
statement->getType() == DsqlCompiledStatement::TYPE_DDL)
2008-02-28 14:48:16 +01:00
{
2009-02-01 23:10:12 +01:00
// Notify Trace API manager about new DDL request cooked.
trace.prepare(res_successful);
return request;
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
2009-11-16 10:18:24 +01:00
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");
fb_print_blr(statement->getBlrData().begin(),
(ULONG) statement->getBlrData().getCount(),
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
2009-11-16 10:18:24 +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,
scratch->getAttachment()->dbb_attachment,
&request->req_request,
2008-03-08 22:20:26 +01:00
length,
statement->getBlrData().begin(),
statement->getSqlText(),
statement->getDebugData().getCount(),
statement->getDebugData().begin(),
isInternalRequest);
}
catch (const Firebird::Exception&)
{
status = tdbb->tdbb_status_vector[1];
2009-02-01 23:10:12 +01:00
trace.prepare(status == isc_no_priv ? res_unauthorized : res_failed);
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)
{
size_t indx, len, warning;
2001-05-23 15:26:42 +02:00
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->getBlrData().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);
2009-02-02 04:35:52 +01:00
2009-02-01 23:10:12 +01:00
// Notify Trace API manager about new request cooked.
trace.prepare(res_successful);
2009-02-02 04:35:52 +01:00
return request;
}
catch (const Firebird::Exception&)
{
trace.prepare(res_failed);
2010-02-09 10:00:51 +01:00
request->req_traced = false;
release_request(tdbb, request, 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
@param copy
**/
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,
const UCHAR* const end,
const bool copy)
2001-05-23 15:26:42 +02:00
{
2009-11-16 10:18:24 +01: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;
if (length && copy)
2009-01-06 06:53:34 +01:00
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
}
// Release a compiled statement.
static void release_statement(DsqlCompiledStatement* statement)
{
if (statement->getParentRequest())
{
dsql_req* parent = statement->getParentRequest();
size_t pos;
if (parent->cursors.find(statement, pos))
parent->cursors.remove(pos);
statement->setParentRequest(NULL);
}
statement->setSqlText(NULL);
statement->getBlrData().free(); // free blr memory
}
/**
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
// If request is parent, orphan the children and release a portion of their requests
2001-05-23 15:26:42 +02:00
for (size_t i = 0; i < request->cursors.getCount(); ++i)
{
DsqlCompiledStatement* child = request->cursors[i];
2009-12-24 13:56:31 +01:00
child->addFlags(DsqlCompiledStatement::FLAG_ORPHAN);
child->setParentRequest(NULL);
Jrd::ContextPoolHolder context(tdbb, &child->getPool());
release_statement(child);
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
2009-12-21 15:56:12 +01:00
const DsqlCompiledStatement* statement = request->getStatement();
2009-12-21 15:56:12 +01:00
release_statement(const_cast<DsqlCompiledStatement*>(statement));
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
if (request->req_flags & dsql_req::FLAG_OPENED_CURSOR) {
close_cursor(tdbb, request);
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
Jrd::Attachment* att = request->req_dbb->dbb_attachment;
2009-02-01 23:10:12 +01:00
const bool need_trace_free = request->req_traced && TraceManager::need_dsql_free(att);
if (need_trace_free)
2009-02-01 23:10:12 +01:00
{
TraceSQLStatementImpl stmt(request, NULL);
TraceManager::event_dsql_free(att, &stmt, DSQL_drop);
}
2009-11-16 10:18:24 +01:00
if (request->req_cursor)
{
2001-05-23 15:26:42 +02:00
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
}
// Release the entire request if explicitly asked for
2001-05-23 15:26:42 +02:00
if (drop)
2009-12-21 15:56:12 +01:00
request->req_dbb->deletePool(&request->getPool());
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,
ULONG 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;
2009-11-16 10:18:24 +01:00
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?
const dsql_msg* message = NULL;
bool messageFound = false;
USHORT first_index = 0;
2009-12-22 01:08:49 +01:00
const DsqlCompiledStatement* statement = request->getStatement();
while (items < end_items && *items != isc_info_end)
{
ULONG length;
USHORT 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:
2009-12-21 15:56:12 +01:00
message = (item == isc_info_sql_select) ?
statement->getReceiveMsg() : statement->getSendMsg();
messageFound = true;
2009-11-16 10:18:24 +01:00
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 (statement->getType())
2009-01-06 06:53:34 +01:00
{
case DsqlCompiledStatement::TYPE_SELECT:
number = isc_info_sql_stmt_select;
break;
case DsqlCompiledStatement::TYPE_SELECT_UPD:
number = isc_info_sql_stmt_select_for_upd;
break;
case DsqlCompiledStatement::TYPE_CREATE_DB:
case DsqlCompiledStatement::TYPE_DDL:
number = isc_info_sql_stmt_ddl;
break;
case DsqlCompiledStatement::TYPE_GET_SEGMENT:
number = isc_info_sql_stmt_get_segment;
break;
case DsqlCompiledStatement::TYPE_PUT_SEGMENT:
number = isc_info_sql_stmt_put_segment;
break;
case DsqlCompiledStatement::TYPE_COMMIT:
case DsqlCompiledStatement::TYPE_COMMIT_RETAIN:
number = isc_info_sql_stmt_commit;
break;
case DsqlCompiledStatement::TYPE_ROLLBACK:
case DsqlCompiledStatement::TYPE_ROLLBACK_RETAIN:
number = isc_info_sql_stmt_rollback;
break;
case DsqlCompiledStatement::TYPE_START_TRANS:
number = isc_info_sql_stmt_start_trans;
break;
case DsqlCompiledStatement::TYPE_INSERT:
number = isc_info_sql_stmt_insert;
break;
case DsqlCompiledStatement::TYPE_UPDATE:
case DsqlCompiledStatement::TYPE_UPDATE_CURSOR:
number = isc_info_sql_stmt_update;
break;
case DsqlCompiledStatement::TYPE_DELETE:
case DsqlCompiledStatement::TYPE_DELETE_CURSOR:
number = isc_info_sql_stmt_delete;
break;
case DsqlCompiledStatement::TYPE_EXEC_PROCEDURE:
number = isc_info_sql_stmt_exec_procedure;
break;
case DsqlCompiledStatement::TYPE_SET_GENERATOR:
number = isc_info_sql_stmt_set_generator;
break;
case DsqlCompiledStatement::TYPE_SAVEPOINT:
number = isc_info_sql_stmt_savepoint;
break;
case DsqlCompiledStatement::TYPE_EXEC_BLOCK:
number = isc_info_sql_stmt_exec_procedure;
break;
case DsqlCompiledStatement::TYPE_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 (statement->getFlags() & DsqlCompiledStatement::FLAG_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:
length = get_request_info(tdbb, request, 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
//UCHAR* buffer_ptr = buffer;
UCHAR* buffer_ptr = info + 3;
// Somebody decided to put a platform-dependent NEWLINE at the beginning,
// see get_rsb_item! This idea predates FB1.
static const size_t minPlan = strlen("\nPLAN (T NATURAL)");
if (info + minPlan + 3 >= end_info)
{
fb_assert(info < end_info);
*info = isc_info_truncated;
info = NULL;
length = 0;
}
else
{
//length = DSQL_get_plan_info(tdbb, request, sizeof(buffer),
// reinterpret_cast<SCHAR**>(&buffer_ptr));
length = DSQL_get_plan_info(tdbb, request, (end_info - info - 4),
reinterpret_cast<SCHAR**>(&buffer_ptr),
false);
2009-01-06 06:53:34 +01:00
}
if (length)
{
if (length > MAX_USHORT)
{
fb_assert(info < end_info);
*info = isc_info_truncated;
info = NULL;
}
else
info = put_item(item, length, buffer_ptr, info, end_info, false);
2009-01-06 06:53:34 +01:00
}
//if (length > sizeof(buffer) || buffer_ptr != buffer) {
// gds__free(buffer_ptr);
//}
2009-01-06 06:53:34 +01:00
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 (messageFound)
2009-01-06 06:53:34 +01:00
{
number = message ? message->msg_index : 0;
2009-01-06 06:53:34 +01:00
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++;
}
info = var_info(message, items, end_describe, info, end_info, first_index,
message == statement->getSendMsg());
2009-01-29 21:36:29 +01:00
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))
{
const SLONG number = info - start_info;
fb_assert(number > 0);
memmove(start_info + 7, start_info, number);
USHORT length = convert(number, buffer);
fb_assert(length == 4); // We only accept SLONG
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
**/
static UCHAR* var_info(const 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,
bool input_message)
2001-05-23 15:26:42 +02:00
{
if (!message || !message->msg_index)
return info;
thread_db* tdbb = JRD_get_thread_data();
2009-12-06 20:11:25 +01:00
Jrd::Attachment* attachment = tdbb->getAttachment();
HalfStaticArray<const dsql_par*, 16> parameters;
2007-01-19 13:11:16 +01:00
for (size_t i = 0; i < message->msg_parameters.getCount(); ++i)
{
const dsql_par* param = message->msg_parameters[i];
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:
if (input_message && (param->par_desc.dsc_flags & DSC_null))
{
sql_type = SQL_NULL;
sql_len = 0;
}
else
{
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;
MetaName 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 (param->par_name.hasData())
2009-11-16 10:18:24 +01:00
{
name = attachment->nameToUserCharSet(tdbb, param->par_name);
length = name.length();
buffer = reinterpret_cast<const UCHAR*>(name.c_str());
}
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 (param->par_rel_name.hasData())
2009-11-16 10:18:24 +01:00
{
name = attachment->nameToUserCharSet(tdbb, param->par_rel_name);
length = name.length();
buffer = reinterpret_cast<const UCHAR*>(name.c_str());
}
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 (param->par_owner_name.hasData())
2009-11-16 10:18:24 +01:00
{
name = attachment->nameToUserCharSet(tdbb, param->par_owner_name);
length = name.length();
buffer = reinterpret_cast<const UCHAR*>(name.c_str());
}
2001-05-23 15:26:42 +02:00
else
length = 0;
break;
case isc_info_sql_relation_alias:
if (param->par_rel_alias.hasData())
2009-11-16 10:18:24 +01:00
{
name = attachment->nameToUserCharSet(tdbb, param->par_rel_alias);
length = name.length();
buffer = reinterpret_cast<const UCHAR*>(name.c_str());
}
else
length = 0;
break;
2003-11-08 00:27:24 +01:00
case isc_info_sql_alias:
if (param->par_alias.hasData())
2009-11-16 10:18:24 +01:00
{
name = attachment->nameToUserCharSet(tdbb, param->par_alias);
length = name.length();
buffer = reinterpret_cast<const UCHAR*>(name.c_str());
}
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;
}
2009-11-16 10:18:24 +01:00
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;
}