8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-28 02:43:03 +01:00
firebird-mirror/src/jrd/par.cpp

1732 lines
45 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Access Method
2003-10-16 10:51:06 +02:00
* MODULE: par.cpp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: BLR Parser
*
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
2002-07-01 18:59:09 +02:00
*
* 27-May-2001 Claudio Valderrama: par_plan() no longer uppercases
* an index's name before doing a lookup of such index.
* 2001.07.28: Added parse code for blr_skip to support LIMIT.
* 2002.09.28 Dmitry Yemanov: Reworked internal_info stuff, enhanced
* exception handling in SPs/triggers,
* implemented ROWS_AFFECTED system variable
* 2002.10.21 Nickolay Samofatov: Added support for explicit pessimistic locks
* 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "MPEXL" port
2002-10-29 17:27:47 +01:00
* 2002.10.29 Mike Nordell - Fixed breakage.
* 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
* 2003.10.05 Dmitry Yemanov: Added support for explicit cursors in PSQL
2008-08-27 11:45:49 +02:00
* 2004.01.16 Vlad Horsun: Added support for default parameters
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:43:34 +02:00
#include <stdio.h>
2001-05-23 15:26:42 +02:00
#include <string.h>
#include "../jrd/jrd.h"
2003-11-08 17:40:17 +01:00
#include "../jrd/ibase.h"
#include "../jrd/ini.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/val.h"
#include "../jrd/align.h"
#include "../jrd/exe.h"
#include "../jrd/extds/ExtDS.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/lls.h"
#include "../jrd/scl.h"
#include "../jrd/req.h"
#include "../jrd/blb.h"
#include "../jrd/intl.h"
#include "../jrd/met.h"
#include "../jrd/cmp_proto.h"
#include "../jrd/cvt_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/fun_proto.h"
2010-10-12 10:02:57 +02:00
#include "../yvalve/gds_proto.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/met_proto.h"
#include "../jrd/par_proto.h"
2003-12-31 06:36:12 +01:00
#include "../common/utils_proto.h"
#include "../jrd/RecordSourceNodes.h"
2007-04-12 17:56:34 +02:00
#include "../jrd/SysFunction.h"
#include "../common/classes/BlrReader.h"
#include "../jrd/Function.h"
#include "../jrd/Attachment.h"
#include "../dsql/BoolNodes.h"
#include "../dsql/ExprNodes.h"
#include "../dsql/StmtNodes.h"
2003-12-31 06:36:12 +01:00
2004-04-29 02:50:02 +02:00
using namespace Jrd;
using namespace Firebird;
2004-04-29 02:50:02 +02:00
#include "gen/codetext.h"
2001-05-23 15:26:42 +02:00
static NodeParseFunc blr_parsers[256] = {NULL};
static CompilerScratch* par_start(thread_db* tdbb, jrd_rel* relation, CompilerScratch* view_csb,
CompilerScratch** csb_ptr, const bool trigger, USHORT flags);
static void par_error(BlrReader& blrReader, const Arg::StatusVector& v, bool isSyntaxError = true);
static PlanNode* par_plan(thread_db*, CompilerScratch*);
static void getBlrVersion(CompilerScratch* csb);
2001-05-23 15:26:42 +02:00
namespace
{
class FetchNode
{
public:
// Parse a FETCH statement, and map it into FOR x IN relation WITH x.DBKEY EQ value ...
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR /*blrOp*/)
{
ForNode* forNode = FB_NEW(pool) ForNode(pool);
// Fake RseNode.
RseNode* rse = forNode->rse = FB_NEW(*tdbb->getDefaultPool()) RseNode(
*tdbb->getDefaultPool());
DmlNode* relationNode = PAR_parse_node(tdbb, csb);
if (relationNode->kind != DmlNode::KIND_REC_SOURCE)
PAR_syntax_error(csb, "TABLE");
RelationSourceNode* relationSource = static_cast<RelationSourceNode*>(relationNode);
if (relationSource->type != RelationSourceNode::TYPE)
PAR_syntax_error(csb, "TABLE");
rse->rse_relations.add(relationSource);
// Fake boolean.
ComparativeBoolNode* booleanNode = FB_NEW(csb->csb_pool) ComparativeBoolNode(
csb->csb_pool, blr_eql);
rse->rse_boolean = booleanNode;
booleanNode->arg2 = PAR_parse_value(tdbb, csb);
RecordKeyNode* dbKeyNode = FB_NEW(csb->csb_pool) RecordKeyNode(csb->csb_pool, blr_dbkey);
dbKeyNode->recStream = relationSource->getStream();
booleanNode->arg1 = dbKeyNode;
// Pick up statement.
forNode->statement = PAR_parse_stmt(tdbb, csb);
return forNode;
}
};
static RegisterNode<FetchNode> regFetch(blr_fetch);
} // namespace
2013-05-21 23:49:26 +02:00
// Common start for PAR_blr and PAR_preparsed_node. Returns the possibly created csb.
static CompilerScratch* par_start(thread_db* tdbb, jrd_rel* relation, CompilerScratch* view_csb,
CompilerScratch** csb_ptr, const bool trigger, USHORT flags)
2001-05-23 15:26:42 +02:00
{
SET_TDBB(tdbb);
CompilerScratch* csb;
2009-11-20 08:03:20 +01:00
if (!(csb_ptr && (csb = *csb_ptr)))
{
size_t count = 5;
2001-05-23 15:26:42 +02:00
if (view_csb)
count += view_csb->csb_rpt.getCapacity();
csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), count);
2001-05-23 15:26:42 +02:00
csb->csb_g_flags |= flags;
}
2009-11-25 10:07:58 +01:00
// If there is a request ptr, this is a trigger. Set up contexts 0 and 1 for
// the target relation
2001-05-23 15:26:42 +02:00
2009-01-20 09:33:59 +01:00
if (trigger)
{
StreamType stream = csb->nextStream();
CompilerScratch::csb_repeat* t1 = CMP_csb_element(csb, 0);
2001-05-23 15:26:42 +02:00
t1->csb_flags |= csb_used | csb_active | csb_trigger;
t1->csb_relation = relation;
t1->csb_stream = stream;
2001-05-23 15:26:42 +02:00
stream = csb->nextStream();
t1 = CMP_csb_element(csb, 1);
2001-05-23 15:26:42 +02:00
t1->csb_flags |= csb_used | csb_active | csb_trigger;
t1->csb_relation = relation;
t1->csb_stream = stream;
2001-05-23 15:26:42 +02:00
}
2009-11-20 08:03:20 +01:00
else if (relation)
{
CompilerScratch::csb_repeat* t1 = CMP_csb_element(csb, 0);
t1->csb_stream = csb->nextStream();
t1->csb_relation = relation;
t1->csb_flags = csb_used | csb_active;
2001-05-23 15:26:42 +02:00
}
2009-01-20 09:33:59 +01:00
if (view_csb)
{
CompilerScratch::rpt_itr ptr = view_csb->csb_rpt.begin();
// AB: csb_n_stream replaced by view_csb->csb_rpt.getCount(), because there could
2008-08-27 11:45:49 +02:00
// be more then just csb_n_stream-numbers that hold data.
// Certainly csb_stream (see PAR_context where the context is retrieved)
const CompilerScratch::rpt_const_itr end = view_csb->csb_rpt.end();
for (StreamType stream = 0; ptr != end; ++ptr, ++stream)
2009-11-20 08:03:20 +01:00
{
CompilerScratch::csb_repeat* t2 = CMP_csb_element(csb, stream);
2001-12-24 03:51:06 +01:00
t2->csb_relation = ptr->csb_relation;
t2->csb_procedure = ptr->csb_procedure;
2001-12-24 03:51:06 +01:00
t2->csb_stream = ptr->csb_stream;
t2->csb_flags = ptr->csb_flags & csb_used;
2001-05-23 15:26:42 +02:00
}
csb->csb_n_stream = view_csb->csb_n_stream;
}
return csb;
}
// Parse blr, returning a compiler scratch block with the results.
// Caller must do pool handling.
DmlNode* PAR_blr(thread_db* tdbb, jrd_rel* relation, const UCHAR* blr, ULONG blr_length,
CompilerScratch* view_csb, CompilerScratch** csb_ptr, JrdStatement** statementPtr,
const bool trigger, USHORT flags)
{
#ifdef CMP_DEBUG
cmp_trace("BLR code given for JRD parsing:");
// CVC: Couldn't find isc_trace_printer, so changed it to gds__trace_printer.
fb_print_blr(blr, blr_length, gds__trace_printer, 0, 0);
#endif
CompilerScratch* csb = par_start(tdbb, relation, view_csb, csb_ptr, trigger, flags);
csb->csb_blr_reader = BlrReader(blr, blr_length);
getBlrVersion(csb);
2001-05-23 15:26:42 +02:00
DmlNode* node = PAR_parse_node(tdbb, csb);
2001-05-23 15:26:42 +02:00
csb->csb_node = node;
if (csb->csb_blr_reader.getByte() != (UCHAR) blr_eoc)
PAR_syntax_error(csb, "end_of_command");
2001-05-23 15:26:42 +02:00
if (statementPtr)
*statementPtr = JrdStatement::makeStatement(tdbb, csb, false);
2001-05-23 15:26:42 +02:00
if (csb_ptr)
*csb_ptr = csb;
else
2001-12-24 03:51:06 +01:00
delete csb;
2001-05-23 15:26:42 +02:00
return node;
}
// Finish parse of memory nodes, returning a compiler scratch block with the results.
// Caller must do pool handling.
void PAR_preparsed_node(thread_db* tdbb, jrd_rel* relation, DmlNode* node,
CompilerScratch* view_csb, CompilerScratch** csb_ptr, JrdStatement** statementPtr,
const bool trigger, USHORT flags)
{
CompilerScratch* csb = par_start(tdbb, relation, view_csb, csb_ptr, trigger, flags);
csb->blrVersion = 5; // blr_version5
csb->csb_node = node;
if (statementPtr)
*statementPtr = JrdStatement::makeStatement(tdbb, csb, false);
if (csb_ptr)
*csb_ptr = csb;
else
delete csb;
}
// PAR_blr equivalent for validation expressions.
// Validation expressions are boolean expressions, but may be prefixed with a blr_stmt_expr.
2013-03-31 20:23:54 +02:00
BoolExprNode* PAR_validation_blr(thread_db* tdbb, jrd_rel* relation, const UCHAR* blr, ULONG blr_length,
CompilerScratch* view_csb, CompilerScratch** csb_ptr, USHORT flags)
{
SET_TDBB(tdbb);
#ifdef CMP_DEBUG
cmp_trace("BLR code given for JRD parsing:");
// CVC: Couldn't find isc_trace_printer, so changed it to gds__trace_printer.
fb_print_blr(blr, blr_length, gds__trace_printer, 0, 0);
#endif
CompilerScratch* csb;
if (!(csb_ptr && (csb = *csb_ptr)))
{
size_t count = 5;
if (view_csb)
count += view_csb->csb_rpt.getCapacity();
csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), count);
csb->csb_g_flags |= flags;
}
// If there is a request ptr, this is a trigger. Set up contexts 0 and 1 for
// the target relation
if (relation)
{
CompilerScratch::csb_repeat* t1 = CMP_csb_element(csb, 0);
t1->csb_stream = csb->nextStream();
t1->csb_relation = relation;
t1->csb_flags = csb_used | csb_active;
}
csb->csb_blr_reader = BlrReader(blr, blr_length);
if (view_csb)
{
CompilerScratch::rpt_itr ptr = view_csb->csb_rpt.begin();
// AB: csb_n_stream replaced by view_csb->csb_rpt.getCount(), because there could
// be more then just csb_n_stream-numbers that hold data.
// Certainly csb_stream (see PAR_context where the context is retrieved)
const CompilerScratch::rpt_const_itr end = view_csb->csb_rpt.end();
for (StreamType stream = 0; ptr != end; ++ptr, ++stream)
{
CompilerScratch::csb_repeat* t2 = CMP_csb_element(csb, stream);
t2->csb_relation = ptr->csb_relation;
t2->csb_procedure = ptr->csb_procedure;
t2->csb_stream = ptr->csb_stream;
t2->csb_flags = ptr->csb_flags & csb_used;
}
csb->csb_n_stream = view_csb->csb_n_stream;
}
getBlrVersion(csb);
if (csb->csb_blr_reader.peekByte() == blr_stmt_expr)
{
2013-04-01 17:37:39 +02:00
// Parse it and ignore. This legacy and broken verb is transformed while parsing expressions.
csb->csb_blr_reader.getByte();
2013-04-01 17:37:39 +02:00
PAR_parse_stmt(tdbb, csb);
}
BoolExprNode* expr = PAR_parse_boolean(tdbb, csb);
if (csb->csb_blr_reader.getByte() != (UCHAR) blr_eoc)
PAR_syntax_error(csb, "end_of_command");
if (csb_ptr)
*csb_ptr = csb;
else
delete csb;
2013-03-31 20:23:54 +02:00
return expr;
}
// Parse a BLR datatype. Return the alignment requirements of the datatype.
USHORT PAR_datatype(BlrReader& blrReader, dsc* desc)
2001-05-23 15:26:42 +02:00
{
desc->clear();
2001-05-23 15:26:42 +02:00
const USHORT dtype = blrReader.getByte();
2009-11-28 20:39:23 +01:00
USHORT textType;
switch (dtype)
{
case blr_text:
desc->makeText(blrReader.getWord(), ttype_dynamic);
desc->dsc_flags |= DSC_no_subtype;
break;
2001-05-23 15:26:42 +02:00
case blr_cstring:
desc->dsc_dtype = dtype_cstring;
desc->dsc_flags |= DSC_no_subtype;
desc->dsc_length = blrReader.getWord();
desc->setTextType(ttype_dynamic);
break;
2001-05-23 15:26:42 +02:00
case blr_varying:
desc->makeVarying(blrReader.getWord(), ttype_dynamic);
desc->dsc_flags |= DSC_no_subtype;
break;
2001-05-23 15:26:42 +02:00
case blr_text2:
textType = blrReader.getWord();
desc->makeText(blrReader.getWord(), textType);
break;
2001-05-23 15:26:42 +02:00
case blr_cstring2:
desc->dsc_dtype = dtype_cstring;
desc->setTextType(blrReader.getWord());
desc->dsc_length = blrReader.getWord();
break;
2001-05-23 15:26:42 +02:00
case blr_varying2:
textType = blrReader.getWord();
desc->makeVarying(blrReader.getWord(), textType);
break;
2001-05-23 15:26:42 +02:00
case blr_short:
desc->dsc_dtype = dtype_short;
desc->dsc_length = sizeof(SSHORT);
desc->dsc_scale = (int) blrReader.getByte();
break;
2001-05-23 15:26:42 +02:00
case blr_long:
desc->dsc_dtype = dtype_long;
desc->dsc_length = sizeof(SLONG);
desc->dsc_scale = (int) blrReader.getByte();
break;
2001-05-23 15:26:42 +02:00
case blr_int64:
desc->dsc_dtype = dtype_int64;
desc->dsc_length = sizeof(SINT64);
desc->dsc_scale = (int) blrReader.getByte();
break;
2001-05-23 15:26:42 +02:00
case blr_quad:
desc->dsc_dtype = dtype_quad;
desc->dsc_length = sizeof(ISC_QUAD);
desc->dsc_scale = (int) blrReader.getByte();
break;
2001-05-23 15:26:42 +02:00
case blr_float:
desc->dsc_dtype = dtype_real;
desc->dsc_length = sizeof(float);
break;
2001-05-23 15:26:42 +02:00
case blr_timestamp:
desc->dsc_dtype = dtype_timestamp;
desc->dsc_length = sizeof(ISC_QUAD);
break;
2001-05-23 15:26:42 +02:00
case blr_sql_date:
desc->dsc_dtype = dtype_sql_date;
desc->dsc_length = type_lengths[dtype_sql_date];
break;
2001-05-23 15:26:42 +02:00
case blr_sql_time:
desc->dsc_dtype = dtype_sql_time;
desc->dsc_length = type_lengths[dtype_sql_time];
break;
2001-05-23 15:26:42 +02:00
case blr_double:
case blr_d_float:
desc->dsc_dtype = dtype_double;
desc->dsc_length = sizeof(double);
break;
2001-05-23 15:26:42 +02:00
case blr_blob2:
2001-05-23 15:26:42 +02:00
desc->dsc_dtype = dtype_blob;
desc->dsc_length = sizeof(ISC_QUAD);
desc->dsc_sub_type = blrReader.getWord();
textType = blrReader.getWord();
desc->dsc_scale = textType & 0xFF; // BLOB character set
desc->dsc_flags = textType & 0xFF00; // BLOB collation
break;
2005-05-28 00:45:31 +02:00
case blr_bool:
desc->makeBoolean();
2001-05-23 15:26:42 +02:00
break;
2005-05-28 00:45:31 +02:00
default:
par_error(blrReader, Arg::Gds(isc_datnotsup));
}
return type_alignments[desc->dsc_dtype];
}
// Parse a BLR descriptor. Return the alignment requirements of the datatype.
USHORT PAR_desc(thread_db* tdbb, CompilerScratch* csb, dsc* desc, ItemInfo* itemInfo)
{
if (itemInfo)
{
itemInfo->nullable = true;
itemInfo->explicitCollation = false;
itemInfo->fullDomain = false;
}
desc->clear();
const USHORT dtype = csb->csb_blr_reader.getByte();
2010-12-18 03:17:06 +01:00
switch (dtype)
{
case blr_not_nullable:
PAR_desc(tdbb, csb, desc, itemInfo);
if (itemInfo)
itemInfo->nullable = false;
break;
case blr_domain_name:
case blr_domain_name2:
{
2014-03-26 20:10:08 +01:00
const bool fullDomain = (csb->csb_blr_reader.getByte() == blr_domain_full);
MetaName* name = FB_NEW(csb->csb_pool) MetaName(csb->csb_pool);
PAR_name(csb, *name);
MetaNamePair namePair(*name, "");
FieldInfo fieldInfo;
bool exist = csb->csb_map_field_info.get(namePair, fieldInfo);
MET_get_domain(tdbb, csb->csb_pool, *name, desc, (exist ? NULL : &fieldInfo));
if (!exist)
csb->csb_map_field_info.put(namePair, fieldInfo);
if (itemInfo)
{
itemInfo->field = namePair;
if (fullDomain)
{
itemInfo->nullable = fieldInfo.nullable;
itemInfo->fullDomain = true;
}
else
itemInfo->nullable = true;
}
if (dtype == blr_domain_name2)
{
const USHORT ttype = csb->csb_blr_reader.getWord();
switch (desc->dsc_dtype)
{
case dtype_cstring:
case dtype_text:
case dtype_varying:
2009-11-28 20:39:23 +01:00
desc->setTextType(ttype);
break;
case dtype_blob:
desc->dsc_scale = ttype & 0xFF; // BLOB character set
desc->dsc_flags = ttype & 0xFF00; // BLOB collation
break;
default:
PAR_error(csb, Arg::Gds(isc_collation_requires_text));
break;
}
}
CompilerScratch::Dependency dependency(obj_field);
dependency.name = name;
csb->csb_dependencies.push(dependency);
break;
}
case blr_column_name:
case blr_column_name2:
2008-01-16 10:41:31 +01:00
{
const bool fullDomain = (csb->csb_blr_reader.getByte() == blr_domain_full);
MetaName* relationName = FB_NEW(csb->csb_pool) MetaName(csb->csb_pool);
PAR_name(csb, *relationName);
MetaName* fieldName = FB_NEW(csb->csb_pool) MetaName(csb->csb_pool);
PAR_name(csb, *fieldName);
2008-01-16 10:41:31 +01:00
MetaNamePair namePair(*relationName, *fieldName);
2008-01-16 10:41:31 +01:00
FieldInfo fieldInfo;
bool exist = csb->csb_map_field_info.get(namePair, fieldInfo);
MET_get_relation_field(tdbb, csb->csb_pool, *relationName, *fieldName, desc,
(exist ? NULL : &fieldInfo));
2008-01-16 10:41:31 +01:00
if (!exist)
csb->csb_map_field_info.put(namePair, fieldInfo);
2008-01-16 10:41:31 +01:00
if (itemInfo)
{
itemInfo->field = namePair;
2008-01-16 10:41:31 +01:00
if (fullDomain)
{
itemInfo->nullable = fieldInfo.nullable;
itemInfo->fullDomain = true;
}
else
itemInfo->nullable = true;
}
if (dtype == blr_column_name2)
{
const USHORT ttype = csb->csb_blr_reader.getWord();
2008-01-16 10:41:31 +01:00
switch (desc->dsc_dtype)
{
case dtype_cstring:
case dtype_text:
case dtype_varying:
2009-11-28 20:39:23 +01:00
desc->setTextType(ttype);
2008-01-16 10:41:31 +01:00
break;
case dtype_blob:
desc->dsc_scale = ttype & 0xFF; // BLOB character set
desc->dsc_flags = ttype & 0xFF00; // BLOB collation
break;
default:
PAR_error(csb, Arg::Gds(isc_collation_requires_text));
2008-01-16 10:41:31 +01:00
break;
}
}
CompilerScratch::Dependency dependency(obj_relation);
dependency.relation = MET_lookup_relation(tdbb, *relationName);
dependency.subName = fieldName;
csb->csb_dependencies.push(dependency);
2008-01-16 10:41:31 +01:00
break;
}
default:
csb->csb_blr_reader.seekBackward(1);
PAR_datatype(csb->csb_blr_reader, desc);
break;
2001-05-23 15:26:42 +02:00
}
if (desc->getTextType() != CS_NONE)
{
CompilerScratch::Dependency dependency(obj_collation);
dependency.number = INTL_TEXT_TYPE(*desc);
csb->csb_dependencies.push(dependency);
}
if (itemInfo)
{
2006-10-02 04:37:45 +02:00
if (dtype == blr_cstring2 || dtype == blr_text2 || dtype == blr_varying2 ||
dtype == blr_blob2 || dtype == blr_domain_name2)
2006-10-02 04:37:45 +02:00
{
itemInfo->explicitCollation = true;
2006-10-02 04:37:45 +02:00
}
}
2001-05-23 15:26:42 +02:00
return type_alignments[desc->dsc_dtype];
}
ValueExprNode* PAR_gen_field(thread_db* tdbb, StreamType stream, USHORT id, bool byId)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P A R _ g e n _ f i e l d
*
**************************************
*
* Functional description
* Generate a field block.
*
**************************************/
SET_TDBB(tdbb);
return FB_NEW(*tdbb->getDefaultPool()) FieldNode(*tdbb->getDefaultPool(), stream, id, byId);
2001-05-23 15:26:42 +02:00
}
ValueExprNode* PAR_make_field(thread_db* tdbb, CompilerScratch* csb, USHORT context,
const MetaName& base_field)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P A R _ m a k e _ f i e l d
*
**************************************
*
* Functional description
* Make up a field node in the permanent pool. This is used
* by MET_scan_relation to handle view fields.
*
**************************************/
SET_TDBB(tdbb);
if (context >= csb->csb_rpt.getCount() || !(csb->csb_rpt[context].csb_flags & csb_used))
return NULL;
const StreamType stream = csb->csb_rpt[context].csb_stream;
2001-05-23 15:26:42 +02:00
jrd_rel* const relation = csb->csb_rpt[stream].csb_relation;
jrd_prc* const procedure = csb->csb_rpt[stream].csb_procedure;
2002-07-01 18:59:09 +02:00
const SSHORT id =
relation ? MET_lookup_field(tdbb, relation, base_field) :
procedure ? PAR_find_proc_field(procedure, base_field) : -1;
2001-05-23 15:26:42 +02:00
if (id < 0)
return NULL;
2012-01-29 03:53:44 +01:00
if (csb->csb_g_flags & csb_get_dependencies)
PAR_dependency(tdbb, csb, stream, id, base_field);
2006-01-13 10:33:40 +01:00
return PAR_gen_field(tdbb, stream, id);
2001-05-23 15:26:42 +02:00
}
// Make a list node out of a stack.
CompoundStmtNode* PAR_make_list(thread_db* tdbb, StmtNodeStack& stack)
2001-05-23 15:26:42 +02:00
{
SET_TDBB(tdbb);
// Count the number of nodes.
USHORT count = stack.getCount();
2001-05-23 15:26:42 +02:00
CompoundStmtNode* node = FB_NEW(*tdbb->getDefaultPool()) CompoundStmtNode(*tdbb->getDefaultPool());
NestConst<StmtNode>* ptr = node->statements.getBuffer(count) + count;
2001-05-23 15:26:42 +02:00
2008-08-27 11:45:49 +02:00
while (stack.hasData())
*--ptr = stack.pop();
2001-05-23 15:26:42 +02:00
return node;
}
CompilerScratch* PAR_parse(thread_db* tdbb, const UCHAR* blr, ULONG blr_length,
bool internal_flag, ULONG dbginfo_length, const UCHAR* dbginfo)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P A R _ p a r s e
*
**************************************
*
* Functional description
* Parse blr, returning a compiler scratch block with the results.
*
**************************************/
SET_TDBB(tdbb);
CompilerScratch* csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5);
csb->csb_blr_reader = BlrReader(blr, blr_length);
2001-05-23 15:26:42 +02:00
if (internal_flag)
csb->csb_g_flags |= csb_internal;
getBlrVersion(csb);
2001-05-23 15:26:42 +02:00
if (dbginfo_length > 0)
DBG_parse_debug_info(dbginfo_length, dbginfo, *csb->csb_dbg_info);
DmlNode* node = PAR_parse_node(tdbb, csb);
2001-05-23 15:26:42 +02:00
csb->csb_node = node;
if (csb->csb_blr_reader.getByte() != (UCHAR) blr_eoc)
PAR_syntax_error(csb, "end_of_command");
2001-05-23 15:26:42 +02:00
return csb;
}
SLONG PAR_symbol_to_gdscode(const Firebird::string& name)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P A R _ s y m b o l _ t o _ g d s c o d e
*
**************************************
*
* Functional description
* Symbolic ASCII names are used in blr for posting and handling
* exceptions. They are also used to identify error codes
* within system triggers in a database.
*
* Returns the gds error status code for the given symbolic
* name, or 0 if not found.
*
* Symbolic names may be null or space terminated.
*
**************************************/
2009-11-20 08:03:20 +01:00
for (int i = 0; codes[i].code_number; ++i)
{
if (name == codes[i].code_string) {
2001-05-23 15:26:42 +02:00
return codes[i].code_number;
2002-10-29 17:27:47 +01:00
}
}
2001-05-23 15:26:42 +02:00
return 0;
}
2008-05-20 02:58:14 +02:00
// Registers a parse function (DmlNode creator) for a BLR code.
void PAR_register(UCHAR blr, NodeParseFunc parseFunc)
{
fb_assert(!blr_parsers[blr] || blr_parsers[blr] == parseFunc);
blr_parsers[blr] = parseFunc;
}
// We've got a blr error other than a syntax error. Handle it.
static void par_error(BlrReader& blrReader, const Arg::StatusVector& v, bool isSyntaxError)
2001-05-23 15:26:42 +02:00
{
2008-09-05 14:35:07 +02:00
fb_assert(v.value()[0] == isc_arg_gds);
2009-11-25 10:07:58 +01:00
// Don't bother to pass tdbb for error handling
thread_db* tdbb = JRD_get_thread_data();
2001-05-23 15:26:42 +02:00
2009-12-03 05:33:46 +01:00
if (isSyntaxError)
{
blrReader.seekBackward(1);
2009-12-03 05:33:46 +01:00
Arg::Gds p(isc_invalid_blr);
p << Arg::Num(blrReader.getOffset());
2009-12-03 05:33:46 +01:00
p.append(v);
p.copyTo(tdbb->tdbb_status_vector);
}
else
v.copyTo(tdbb->tdbb_status_vector);
ERR_make_permanent(tdbb->tdbb_status_vector);
2001-05-23 15:26:42 +02:00
2009-11-25 10:07:58 +01:00
// Give up whatever we were doing and return to the user.
2001-05-23 15:26:42 +02:00
ERR_punt();
}
// We've got a blr error other than a syntax error. Handle it.
void PAR_error(CompilerScratch* csb, const Arg::StatusVector& v, bool isSyntaxError)
{
par_error(csb->csb_blr_reader, v, isSyntaxError);
}
2001-05-23 15:26:42 +02:00
// Look for named field in procedure output fields.
SSHORT PAR_find_proc_field(const jrd_prc* procedure, const Firebird::MetaName& name)
2001-05-23 15:26:42 +02:00
{
const Array<NestConst<Parameter> >& list = procedure->getOutputFields();
Array<NestConst<Parameter> >::const_iterator ptr = list.begin();
for (const Array<NestConst<Parameter> >::const_iterator end = list.end(); ptr < end; ++ptr)
2003-12-22 11:00:59 +01:00
{
const Parameter* param = *ptr;
if (name == param->prm_name)
2001-05-23 15:26:42 +02:00
return param->prm_number;
}
return -1;
}
// Parse a counted argument list, given the count.
ValueListNode* PAR_args(thread_db* tdbb, CompilerScratch* csb, UCHAR count,
USHORT allocCount)
2001-05-23 15:26:42 +02:00
{
SET_TDBB(tdbb);
2010-01-26 09:20:27 +01:00
fb_assert(allocCount >= count);
MemoryPool& pool = *tdbb->getDefaultPool();
ValueListNode* node = FB_NEW(pool) ValueListNode(pool, allocCount);
2012-05-03 18:43:29 +02:00
NestConst<ValueExprNode>* ptr = node->items.begin();
2001-05-23 15:26:42 +02:00
2009-11-20 08:03:20 +01:00
if (count)
{
do
{
*ptr++ = PAR_parse_value(tdbb, csb);
2003-12-22 11:00:59 +01:00
} while (--count);
}
2001-05-23 15:26:42 +02:00
return node;
}
// Parse a counted argument list.
ValueListNode* PAR_args(thread_db* tdbb, CompilerScratch* csb)
{
SET_TDBB(tdbb);
UCHAR count = csb->csb_blr_reader.getByte();
return PAR_args(tdbb, csb, count, count);
}
StreamType PAR_context(CompilerScratch* csb, SSHORT* context_ptr)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P A R _ c o n t e x t
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
2008-08-27 11:45:49 +02:00
* Introduce a new context into the system. This involves
* assigning a stream and possibly extending the compile
2001-05-23 15:26:42 +02:00
* scratch block.
*
**************************************/
// CVC: Bottleneck
const SSHORT context = (unsigned int) csb->csb_blr_reader.getByte();
if (context_ptr)
*context_ptr = context;
2006-12-22 15:34:53 +01:00
CompilerScratch::csb_repeat* tail = CMP_csb_element(csb, context);
2009-11-20 08:03:20 +01:00
if (tail->csb_flags & csb_used)
{
if (csb->csb_g_flags & csb_reuse_context) {
return tail->csb_stream;
}
2008-02-24 04:23:40 +01:00
PAR_error(csb, Arg::Gds(isc_ctxinuse));
}
const StreamType stream = csb->nextStream(false);
if (stream >= MAX_STREAMS)
{
PAR_error(csb, Arg::Gds(isc_too_many_contexts));
}
2001-05-23 15:26:42 +02:00
tail->csb_flags |= csb_used;
tail->csb_stream = stream;
2001-05-23 15:26:42 +02:00
2006-12-22 15:34:53 +01:00
CMP_csb_element(csb, stream);
2001-05-23 15:26:42 +02:00
return stream;
}
void PAR_dependency(thread_db* tdbb, CompilerScratch* csb, StreamType stream, SSHORT id,
const MetaName& field_name)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P A R _ d e p e n d e n c y
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Register a field, relation, procedure or exception reference
* as a dependency.
*
**************************************/
SET_TDBB(tdbb);
CompilerScratch::Dependency dependency(0);
2009-11-20 08:03:20 +01:00
if (csb->csb_rpt[stream].csb_relation)
{
dependency.relation = csb->csb_rpt[stream].csb_relation;
// How do I determine reliably this is a view?
// At this time, rel_view_rse is still null.
//if (is_view)
// dependency.objType = obj_view;
//else
dependency.objType = obj_relation;
2001-05-23 15:26:42 +02:00
}
2009-11-20 08:03:20 +01:00
else if (csb->csb_rpt[stream].csb_procedure)
{
if (csb->csb_rpt[stream].csb_procedure->isSubRoutine())
return;
dependency.procedure = csb->csb_rpt[stream].csb_procedure;
dependency.objType = obj_procedure;
2001-05-23 15:26:42 +02:00
}
2009-11-20 08:03:20 +01:00
if (field_name.length() > 0)
dependency.subName = FB_NEW(*tdbb->getDefaultPool()) MetaName(*tdbb->getDefaultPool(), field_name);
2009-11-20 08:03:20 +01:00
else if (id >= 0)
dependency.subNumber = id;
2001-05-23 15:26:42 +02:00
csb->csb_dependencies.push(dependency);
2001-05-23 15:26:42 +02:00
}
USHORT PAR_name(CompilerScratch* csb, Firebird::MetaName& name)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P A R _ n a m e
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Parse a counted string, returning count.
*
**************************************/
size_t l = csb->csb_blr_reader.getByte();
2005-05-17 20:20:07 +02:00
// Check for overly long identifiers at BLR parse stage to prevent unwanted
// surprises in deeper layers of the engine.
2009-11-20 08:03:20 +01:00
if (l > MAX_SQL_IDENTIFIER_LEN)
{
2005-05-17 20:20:07 +02:00
SqlIdentifier st;
char* s = st;
l = MAX_SQL_IDENTIFIER_LEN;
2009-08-05 04:12:46 +02:00
while (l--)
*s++ = csb->csb_blr_reader.getByte();
2005-05-17 20:20:07 +02:00
*s = 0;
ERR_post(Arg::Gds(isc_identifier_too_long) << Arg::Str(st));
}
char* s = name.getBuffer(l);
2008-08-27 11:45:49 +02:00
while (l--)
*s++ = csb->csb_blr_reader.getByte();
return name.length();
2001-05-23 15:26:42 +02:00
}
size_t PAR_name(CompilerScratch* csb, Firebird::string& name)
{
/**************************************
*
* P A R _ n a m e
*
**************************************
*
* Functional description
2008-08-27 11:45:49 +02:00
* Parse a counted string of virtually unlimited size
* (up to 64K, actually <= 255), returning count.
*
**************************************/
size_t l = csb->csb_blr_reader.getByte();
char* s = name.getBuffer(l);
2008-08-27 11:45:49 +02:00
while (l--)
*s++ = csb->csb_blr_reader.getByte();
return name.length();
}
static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p a r _ p l a n
*
**************************************
*
* Functional description
2008-08-27 11:45:49 +02:00
* Parse an access plan expression.
* At this stage we are just generating the
* parse tree and checking contexts
2001-05-23 15:26:42 +02:00
* and indices.
*
**************************************/
SET_TDBB(tdbb);
USHORT node_type = (USHORT) csb->csb_blr_reader.getByte();
2001-05-23 15:26:42 +02:00
2009-11-25 10:07:58 +01:00
// a join type indicates a cross of two or more streams
2001-05-23 15:26:42 +02:00
2009-11-20 08:03:20 +01:00
if (node_type == blr_join || node_type == blr_merge)
{
// CVC: bottleneck
int count = (USHORT) csb->csb_blr_reader.getByte();
PlanNode* plan = FB_NEW(csb->csb_pool) PlanNode(csb->csb_pool, PlanNode::TYPE_JOIN);
while (count-- > 0)
plan->subNodes.add(par_plan(tdbb, csb));
2001-05-23 15:26:42 +02:00
return plan;
}
2009-11-25 10:07:58 +01:00
// we have hit a stream; parse the context number and access type
2001-05-23 15:26:42 +02:00
2009-01-20 09:33:59 +01:00
if (node_type == blr_retrieve)
{
PlanNode* plan = FB_NEW(csb->csb_pool) PlanNode(csb->csb_pool, PlanNode::TYPE_RETRIEVE);
2001-05-23 15:26:42 +02:00
2009-11-25 10:07:58 +01:00
// parse the relation name and context--the relation
// itself is redundant except in the case of a view,
// in which case the base relation (and alias) must be specified
2001-05-23 15:26:42 +02:00
USHORT n = (unsigned int) csb->csb_blr_reader.getByte();
2008-12-22 10:00:05 +01:00
if (n != blr_relation && n != blr_relation2 && n != blr_rid && n != blr_rid2)
PAR_syntax_error(csb, "TABLE");
2001-05-23 15:26:42 +02:00
// don't make RelationSourceNode::parse() parse the context, because
2009-11-25 10:07:58 +01:00
// this would add a new context; while this is a reference to
// an existing context
2001-05-23 15:26:42 +02:00
plan->relationNode = RelationSourceNode::parse(tdbb, csb, n, false);
jrd_rel* relation = plan->relationNode->relation;
2001-05-23 15:26:42 +02:00
// CVC: bottleneck
n = csb->csb_blr_reader.getByte();
if (n >= csb->csb_rpt.getCount() || !(csb->csb_rpt[n].csb_flags & csb_used))
PAR_error(csb, Arg::Gds(isc_ctxnotdef));
const StreamType stream = csb->csb_rpt[n].csb_stream;
2001-05-23 15:26:42 +02:00
plan->relationNode->setStream(stream);
plan->relationNode->context = n;
2001-05-23 15:26:42 +02:00
2009-11-25 10:07:58 +01:00
// Access plan types (sequential is default)
2001-05-23 15:26:42 +02:00
node_type = (USHORT) csb->csb_blr_reader.getByte();
MetaName name;
2008-08-27 11:45:49 +02:00
2009-01-20 09:33:59 +01:00
switch (node_type)
{
case blr_navigational:
{
plan->accessType = FB_NEW(csb->csb_pool) PlanNode::AccessType(csb->csb_pool,
PlanNode::AccessType::TYPE_NAVIGATIONAL);
2001-05-23 15:26:42 +02:00
2009-11-25 10:07:58 +01:00
// pick up the index name and look up the appropriate ids
2001-05-23 15:26:42 +02:00
PAR_name(csb, name);
/* CVC: We can't do this. Index names are identifiers.
for (p = name; *p; *p++)
*p = UPPER (*p);
*/
2003-12-22 11:00:59 +01:00
SLONG relation_id;
SSHORT idx_status;
2008-12-22 10:00:05 +01:00
const SLONG index_id = MET_lookup_index_name(tdbb, name, &relation_id, &idx_status);
2001-05-23 15:26:42 +02:00
2008-12-22 10:00:05 +01:00
if (idx_status == MET_object_unknown || idx_status == MET_object_inactive)
2003-12-22 11:00:59 +01:00
{
if (tdbb->getAttachment()->att_flags & ATT_gbak_attachment)
2007-12-05 01:03:15 +01:00
{
PAR_warning(Arg::Warning(isc_indexname) << Arg::Str(name) <<
Arg::Str(relation->rel_name));
2007-12-05 01:03:15 +01:00
}
2001-05-23 15:26:42 +02:00
else
2007-12-05 01:03:15 +01:00
{
PAR_error(csb, Arg::Gds(isc_indexname) << Arg::Str(name) <<
Arg::Str(relation->rel_name));
2007-12-05 01:03:15 +01:00
}
}
2001-05-23 15:26:42 +02:00
2009-11-25 10:07:58 +01:00
// save both the relation id and the index id, since
// the relation could be a base relation of a view;
// save the index name also, for convenience
2001-05-23 15:26:42 +02:00
PlanNode::AccessItem& item = plan->accessType->items.add();
item.relationId = relation_id;
item.indexId = index_id;
item.indexName = name;
if (csb->csb_g_flags & csb_get_dependencies)
{
CompilerScratch::Dependency dependency(obj_index);
dependency.name = &item.indexName;
csb->csb_dependencies.push(dependency);
}
if (csb->csb_blr_reader.peekByte() != blr_indices)
break;
// dimitr: FALL INTO, if the plan item is ORDER ... INDEX (...)
2001-05-23 15:26:42 +02:00
}
case blr_indices:
{
if (plan->accessType)
csb->csb_blr_reader.getByte(); // skip blr_indices
else
{
plan->accessType = FB_NEW(csb->csb_pool) PlanNode::AccessType(csb->csb_pool,
PlanNode::AccessType::TYPE_INDICES);
}
int count = (USHORT) csb->csb_blr_reader.getByte();
2009-11-25 10:07:58 +01:00
// pick up the index names and look up the appropriate ids
while (count-- > 0)
2009-01-20 09:33:59 +01:00
{
PAR_name(csb, name);
/* Nickolay Samofatov: We can't do this. Index names are identifiers.
for (p = name; *p; *p++)
*p = UPPER(*p);
*/
SLONG relation_id;
SSHORT idx_status;
2008-12-22 10:00:05 +01:00
const SLONG index_id = MET_lookup_index_name(tdbb, name, &relation_id, &idx_status);
2008-12-22 10:00:05 +01:00
if (idx_status == MET_object_unknown || idx_status == MET_object_inactive)
{
if (tdbb->getAttachment()->att_flags & ATT_gbak_attachment)
2007-12-05 01:03:15 +01:00
{
PAR_warning(Arg::Warning(isc_indexname) << Arg::Str(name) <<
Arg::Str(relation->rel_name));
2007-12-05 01:03:15 +01:00
}
else
2007-12-05 01:03:15 +01:00
{
PAR_error(csb, Arg::Gds(isc_indexname) << Arg::Str(name) <<
Arg::Str(relation->rel_name));
2007-12-05 01:03:15 +01:00
}
}
2009-11-25 10:07:58 +01:00
// save both the relation id and the index id, since
// the relation could be a base relation of a view;
// save the index name also, for convenience
PlanNode::AccessItem& item = plan->accessType->items.add();
item.relationId = relation_id;
item.indexId = index_id;
item.indexName = name;
if (csb->csb_g_flags & csb_get_dependencies)
{
CompilerScratch::Dependency dependency(obj_index);
dependency.name = &item.indexName;
csb->csb_dependencies.push(dependency);
}
}
}
break;
case blr_sequential:
break;
default:
PAR_syntax_error(csb, "access type");
}
2001-05-23 15:26:42 +02:00
return plan;
}
PAR_syntax_error(csb, "plan item");
2009-11-25 10:07:58 +01:00
return NULL; // Added to remove compiler warning
2001-05-23 15:26:42 +02:00
}
// Parse some procedure parameters.
void PAR_procedure_parms(thread_db* tdbb, CompilerScratch* csb, jrd_prc* procedure,
MessageNode** message_ptr, ValueListNode** sourceList, ValueListNode** targetList, bool input_flag)
2001-05-23 15:26:42 +02:00
{
SET_TDBB(tdbb);
SLONG count = csb->csb_blr_reader.getWord();
const SLONG inputCount = procedure->getInputFields().getCount();
2001-05-23 15:26:42 +02:00
2009-11-25 10:07:58 +01:00
// Check to see if the parameter count matches
2008-08-27 11:45:49 +02:00
if (input_flag ?
(count < (inputCount - procedure->getDefaultCount()) || (count > inputCount) ) :
(count != SLONG(procedure->getOutputFields().getCount())))
2003-12-22 11:00:59 +01:00
{
2010-04-14 10:30:29 +02:00
PAR_error(csb, Arg::Gds(input_flag ? isc_prcmismat : isc_prc_out_param_mismatch) <<
Arg::Str(procedure->getName().toString()));
2001-05-23 15:26:42 +02:00
}
if (count || input_flag && procedure->getDefaultCount())
2009-01-20 09:33:59 +01:00
{
MemoryPool& pool = *tdbb->getDefaultPool();
2009-11-25 10:07:58 +01:00
// We have a few parameters. Get on with creating the message block
2003-12-22 11:00:59 +01:00
USHORT n = ++csb->csb_msg_number;
if (n < 2)
csb->csb_msg_number = n = 2;
CompilerScratch::csb_repeat* tail = CMP_csb_element(csb, n);
MessageNode* message = tail->csb_message = *message_ptr = FB_NEW(pool) MessageNode(pool);
message->messageNumber = n;
const Format* format = input_flag ? procedure->getInputFormat() : procedure->getOutputFormat();
/* dimitr: procedure (with its parameter formats) is allocated out of
its own pool (prc_request->req_pool) and can be freed during
the cache cleanup (MET_clear_cache). Since the current
tdbb default pool is different from the procedure's one,
it's dangerous to copy a pointer from one request to another.
As an experiment, I've decided to copy format by value
2004-03-30 06:10:52 +02:00
instead of copying the reference. Since Format structure
doesn't contain any pointers, it should be safe to use a
default assignment operator which does a simple byte copy.
This change fixes one serious bug in the current codebase.
I think that this situation can (and probably should) be
handled by the metadata cache (via incrementing prc_use_count)
to avoid unexpected cache cleanups, but that area is out of my
knowledge. So this fix should be considered a temporary solution.
message->format = format;
*/
Format* fmt_copy = Format::newFormat(pool, format->fmt_count);
*fmt_copy = *format;
message->format = fmt_copy;
2009-11-25 10:07:58 +01:00
// --- end of fix ---
2010-04-14 10:30:29 +02:00
n = format->fmt_count / 2;
ValueListNode* sourceValues = *sourceList = FB_NEW(pool) ValueListNode(pool, n);
ValueListNode* targetValues = *targetList = FB_NEW(pool) ValueListNode(pool, n);
NestConst<ValueExprNode>* sourcePtr =
2012-05-03 18:43:29 +02:00
input_flag ? sourceValues->items.begin() : targetValues->items.begin();
NestConst<ValueExprNode>* targetPtr =
2012-05-03 18:43:29 +02:00
input_flag ? targetValues->items.begin() : sourceValues->items.begin();
2009-01-20 09:33:59 +01:00
for (USHORT i = 0; n; count--, n--)
{
2008-08-27 11:45:49 +02:00
// default value for parameter
if (count <= 0 && input_flag)
2009-11-20 08:03:20 +01:00
{
Parameter* parameter = procedure->getInputFields()[inputCount - n];
*sourcePtr++ = CMP_clone_node(tdbb, csb, parameter->prm_default_value);
2004-01-16 13:59:16 +01:00
}
else
*sourcePtr++ = PAR_parse_value(tdbb, csb);
2010-10-12 10:02:57 +02:00
ParameterNode* paramNode = FB_NEW(csb->csb_pool) ParameterNode(csb->csb_pool);
paramNode->message = message;
paramNode->argNumber = i++;
2010-10-12 10:02:57 +02:00
ParameterNode* paramFlagNode = FB_NEW(csb->csb_pool) ParameterNode(csb->csb_pool);
paramFlagNode->message = message;
paramFlagNode->argNumber = i++;
2010-10-12 10:02:57 +02:00
paramNode->argFlag = paramFlagNode;
2010-10-12 10:02:57 +02:00
*targetPtr++ = paramNode;
2010-10-12 10:02:57 +02:00
}
2003-12-22 11:00:59 +01:00
}
else if (input_flag ? inputCount : procedure->getOutputFields().getCount())
2010-10-12 10:02:57 +02:00
{
PAR_error(csb, Arg::Gds(input_flag ? isc_prcmismat : isc_prc_out_param_mismatch) <<
Arg::Str(procedure->getName().toString()));
2010-10-12 10:02:57 +02:00
}
2001-05-23 15:26:42 +02:00
}
// Parse a RecordSourceNode.
RecordSourceNode* PAR_parseRecordSource(thread_db* tdbb, CompilerScratch* csb)
{
SET_TDBB(tdbb);
const SSHORT blrOp = csb->csb_blr_reader.getByte();
switch (blrOp)
{
case blr_pid:
case blr_pid2:
case blr_procedure:
case blr_procedure2:
case blr_procedure3:
case blr_procedure4:
case blr_subproc:
return ProcedureSourceNode::parse(tdbb, csb, blrOp);
case blr_rse:
case blr_rs_stream:
return PAR_rse(tdbb, csb, blrOp);
case blr_relation:
case blr_rid:
case blr_relation2:
case blr_rid2:
return RelationSourceNode::parse(tdbb, csb, blrOp, true);
case blr_union:
case blr_recurse:
return UnionSourceNode::parse(tdbb, csb, blrOp);
case blr_window:
return WindowSourceNode::parse(tdbb, csb);
case blr_aggregate:
return AggregateSourceNode::parse(tdbb, csb);
default:
PAR_syntax_error(csb, "record source");
}
return NULL; // silence warning
}
// Parse a record selection expression.
RseNode* PAR_rse(thread_db* tdbb, CompilerScratch* csb, SSHORT rse_op)
2001-05-23 15:26:42 +02:00
{
SET_TDBB(tdbb);
int count = (unsigned int) csb->csb_blr_reader.getByte();
RseNode* rse = FB_NEW(*tdbb->getDefaultPool()) RseNode(*tdbb->getDefaultPool());
2001-05-23 15:26:42 +02:00
2009-11-20 08:03:20 +01:00
while (--count >= 0)
rse->rse_relations.add(PAR_parseRecordSource(tdbb, csb));
2001-05-23 15:26:42 +02:00
2009-01-20 09:33:59 +01:00
while (true)
{
const UCHAR op = csb->csb_blr_reader.getByte();
2009-01-20 09:33:59 +01:00
switch (op)
{
2001-05-23 15:26:42 +02:00
case blr_boolean:
rse->rse_boolean = PAR_parse_boolean(tdbb, csb);
2001-05-23 15:26:42 +02:00
break;
case blr_first:
if (rse_op == blr_rs_stream)
PAR_syntax_error(csb, "RecordSelExpr stream clause");
rse->rse_first = PAR_parse_value(tdbb, csb);
rse->flags |= RseNode::FLAG_OPT_FIRST_ROWS;
2001-05-23 15:26:42 +02:00
break;
case blr_skip:
if (rse_op == blr_rs_stream)
PAR_syntax_error(csb, "RecordSelExpr stream clause");
rse->rse_skip = PAR_parse_value(tdbb, csb);
break;
2002-07-01 18:59:09 +02:00
2001-05-23 15:26:42 +02:00
case blr_sort:
if (rse_op == blr_rs_stream)
PAR_syntax_error(csb, "RecordSelExpr stream clause");
csb->csb_blr_reader.seekBackward(1);
rse->rse_sorted = PAR_sort(tdbb, csb, op, false);
2001-05-23 15:26:42 +02:00
break;
case blr_project:
if (rse_op == blr_rs_stream)
PAR_syntax_error(csb, "RecordSelExpr stream clause");
csb->csb_blr_reader.seekBackward(1);
rse->rse_projection = PAR_sort(tdbb, csb, op, false);
2001-05-23 15:26:42 +02:00
break;
case blr_join_type:
2003-12-22 11:00:59 +01:00
{
const USHORT jointype = (USHORT) csb->csb_blr_reader.getByte();
2003-12-22 11:00:59 +01:00
rse->rse_jointype = jointype;
if (jointype != blr_inner &&
jointype != blr_left &&
jointype != blr_right &&
2008-12-18 12:29:10 +01:00
jointype != blr_full)
2003-12-22 11:00:59 +01:00
{
PAR_syntax_error(csb, "join type clause");
2003-12-22 11:00:59 +01:00
}
break;
}
2001-05-23 15:26:42 +02:00
case blr_plan:
rse->rse_plan = par_plan(tdbb, csb);
break;
2008-08-27 11:45:49 +02:00
case blr_writelock:
rse->flags |= RseNode::FLAG_WRITELOCK;
break;
2001-05-23 15:26:42 +02:00
default:
2009-01-20 09:33:59 +01:00
if (op == (UCHAR) blr_end)
{
2009-11-25 10:07:58 +01:00
// An outer join is only allowed when the stream count is 2
// and a boolean expression has been supplied
2001-05-23 15:26:42 +02:00
if (!rse->rse_jointype || (rse->rse_relations.getCount() == 2 && rse->rse_boolean))
{
2008-02-24 04:23:40 +01:00
// Convert right outer joins to left joins to avoid
// RIGHT JOIN handling at lower engine levels
2009-11-20 08:03:20 +01:00
if (rse->rse_jointype == blr_right)
{
2008-02-24 04:23:40 +01:00
// Swap sub-streams
RecordSourceNode* temp = rse->rse_relations[0];
rse->rse_relations[0] = rse->rse_relations[1];
rse->rse_relations[1] = temp;
2008-02-24 04:23:40 +01:00
rse->rse_jointype = blr_left;
}
return rse;
}
2001-05-23 15:26:42 +02:00
}
PAR_syntax_error(csb, ((rse_op == blr_rs_stream) ?
"RecordSelExpr stream clause" :
2001-07-12 07:46:06 +02:00
"record selection expression clause"));
2001-05-23 15:26:42 +02:00
}
2003-12-22 11:00:59 +01:00
}
2001-05-23 15:26:42 +02:00
}
// Parse a record selection expression.
RseNode* PAR_rse(thread_db* tdbb, CompilerScratch* csb)
{
SET_TDBB(tdbb);
const SSHORT blrOp = csb->csb_blr_reader.getByte();
switch (blrOp)
{
case blr_rse:
case blr_rs_stream:
return PAR_rse(tdbb, csb, blrOp);
case blr_singular:
{
RseNode* rseNode = PAR_rse(tdbb, csb);
rseNode->flags |= RseNode::FLAG_SINGULAR;
return rseNode;
}
case blr_scrollable:
{
RseNode* rseNode = PAR_rse(tdbb, csb);
rseNode->flags |= RseNode::FLAG_SCROLLABLE;
return rseNode;
}
default:
PAR_syntax_error(csb, "RecordSelExpr");
}
return NULL; // warning
}
// Parse a sort clause (sans header byte). This is used for blr_sort, blr_project and blr_group_by.
SortNode* PAR_sort(thread_db* tdbb, CompilerScratch* csb, UCHAR expectedBlr,
bool nullForEmpty)
2001-05-23 15:26:42 +02:00
{
SET_TDBB(tdbb);
const UCHAR blrOp = csb->csb_blr_reader.getByte();
if (blrOp != expectedBlr)
{
char s[20];
sprintf(s, "blr code %d", expectedBlr);
PAR_syntax_error(csb, s);
}
USHORT count = csb->csb_blr_reader.getByte();
if (count == 0 && nullForEmpty)
return NULL;
SortNode* sort = PAR_sort_internal(tdbb, csb, blrOp, count);
if (blrOp != blr_sort)
sort->unique = true;
return sort;
}
// Parse the internals of a sort clause. This is used for blr_sort, blr_project, blr_group_by
// and blr_partition_by.
SortNode* PAR_sort_internal(thread_db* tdbb, CompilerScratch* csb, UCHAR blrOp,
USHORT count)
{
SET_TDBB(tdbb);
SortNode* sort = FB_NEW(*tdbb->getDefaultPool()) SortNode(
*tdbb->getDefaultPool());
NestConst<ValueExprNode>* ptr = sort->expressions.getBuffer(count);
bool* ptr2 = sort->descending.getBuffer(count);
int* ptr3 = sort->nullOrder.getBuffer(count);
while (count-- > 0)
2009-01-20 09:33:59 +01:00
{
if (blrOp == blr_sort)
2009-11-20 08:03:20 +01:00
{
UCHAR code = csb->csb_blr_reader.getByte();
2009-01-20 09:33:59 +01:00
switch (code)
{
case blr_nullsfirst:
*ptr3++ = rse_nulls_first;
code = csb->csb_blr_reader.getByte();
break;
case blr_nullslast:
*ptr3++ = rse_nulls_last;
code = csb->csb_blr_reader.getByte();
break;
default:
*ptr3++ = rse_nulls_default;
}
2008-08-27 11:45:49 +02:00
*ptr2++ = (code == blr_descending);
}
else
{
*ptr2++ = false; // ascending
*ptr3++ = rse_nulls_default;
2002-09-10 20:34:00 +02:00
}
*ptr++ = PAR_parse_value(tdbb, csb);
2001-05-23 15:26:42 +02:00
}
return sort;
2001-05-23 15:26:42 +02:00
}
// Parse a boolean node.
BoolExprNode* PAR_parse_boolean(thread_db* tdbb, CompilerScratch* csb)
{
DmlNode* node = PAR_parse_node(tdbb, csb);
if (node->kind != DmlNode::KIND_BOOLEAN)
PAR_syntax_error(csb, "boolean");
2010-10-12 10:02:57 +02:00
return static_cast<BoolExprNode*>(node);
}
// Parse a value node.
ValueExprNode* PAR_parse_value(thread_db* tdbb, CompilerScratch* csb)
{
DmlNode* node = PAR_parse_node(tdbb, csb);
if (node->kind != DmlNode::KIND_VALUE)
PAR_syntax_error(csb, "value");
return static_cast<ValueExprNode*>(node);
}
// Parse a statement node.
StmtNode* PAR_parse_stmt(thread_db* tdbb, CompilerScratch* csb)
{
DmlNode* node = PAR_parse_node(tdbb, csb);
if (node->kind != DmlNode::KIND_STATEMENT)
PAR_syntax_error(csb, "statement");
return static_cast<StmtNode*>(node);
}
// Parse a BLR node.
DmlNode* PAR_parse_node(thread_db* tdbb, CompilerScratch* csb)
2001-05-23 15:26:42 +02:00
{
SET_TDBB(tdbb);
const ULONG blr_offset = csb->csb_blr_reader.getOffset();
const SSHORT blr_operator = csb->csb_blr_reader.getByte();
2001-05-23 15:26:42 +02:00
if (blr_operator < 0 || blr_operator >= FB_NELEM(blr_parsers))
2009-11-20 08:03:20 +01:00
{
// NS: This error string is correct, please do not mangle it again and again.
// The whole error message is "BLR syntax error: expected %s at offset %d, encountered %d"
PAR_syntax_error(csb, "valid BLR code");
2002-07-01 18:59:09 +02:00
}
2001-05-23 15:26:42 +02:00
2009-11-25 10:07:58 +01:00
// Dispatch on operator type.
2001-05-23 15:26:42 +02:00
2009-01-20 09:33:59 +01:00
switch (blr_operator)
{
case blr_rse:
case blr_rs_stream:
case blr_singular:
case blr_scrollable:
csb->csb_blr_reader.seekBackward(1);
return PAR_rse(tdbb, csb);
2001-05-23 15:26:42 +02:00
case blr_pid:
case blr_pid2:
case blr_procedure:
case blr_procedure2:
case blr_procedure3:
case blr_procedure4:
case blr_subproc:
case blr_relation:
case blr_rid:
case blr_relation2:
case blr_rid2:
case blr_union:
case blr_recurse:
case blr_window:
case blr_aggregate:
csb->csb_blr_reader.seekBackward(1);
return PAR_parseRecordSource(tdbb, csb);
}
2001-05-23 15:26:42 +02:00
if (!blr_parsers[blr_operator])
PAR_syntax_error(csb, "valid BLR code");
DmlNode* node = blr_parsers[blr_operator](tdbb, *tdbb->getDefaultPool(), csb, blr_operator);
size_t pos = 0;
if (node->kind == DmlNode::KIND_STATEMENT && csb->csb_dbg_info->blrToSrc.find(blr_offset, pos))
{
MapBlrToSrcItem& i = csb->csb_dbg_info->blrToSrc[pos];
StmtNode* stmt = static_cast<StmtNode*>(node);
stmt->hasLineColumn = true;
stmt->line = i.mbs_src_line;
stmt->column = i.mbs_src_col;
}
2001-05-23 15:26:42 +02:00
return node;
}
void PAR_syntax_error(CompilerScratch* csb, const TEXT* string)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P A R _ s y n t a x _ e r r o r
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Post a syntax error message.
*
**************************************/
csb->csb_blr_reader.seekBackward(1);
PAR_error(csb, Arg::Gds(isc_syntaxerr) << Arg::Str(string) <<
2009-08-04 10:58:13 +02:00
Arg::Num(csb->csb_blr_reader.getOffset()) <<
Arg::Num(csb->csb_blr_reader.peekByte()));
2001-05-23 15:26:42 +02:00
}
void PAR_warning(const Arg::StatusVector& v)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P A R _ w a r n i n g
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* This is for GBAK so that we can pass warning messages
* back to the client. DO NOT USE this function until we
* fully implement warning at the engine level.
*
* We will use the status vector like a warning vector. What
2008-08-27 11:45:49 +02:00
* we are going to do is leave the [1] position of the vector
2001-05-23 15:26:42 +02:00
* as 0 so that this will not be treated as an error, and we
* will place our warning message in the consecutive positions.
* It will be up to the caller to check these positions for
* the message.
*
**************************************/
2008-09-05 14:35:07 +02:00
fb_assert(v.value()[0] == isc_arg_warning);
thread_db* tdbb = JRD_get_thread_data();
2001-05-23 15:26:42 +02:00
2009-11-25 10:07:58 +01:00
// Make sure that the [1] position is 0 indicating that no error has occurred
Arg::Gds p(FB_SUCCESS);
2001-05-23 15:26:42 +02:00
2009-11-25 10:07:58 +01:00
// Now place your warning messages
p.append(v);
2001-05-23 15:26:42 +02:00
2009-11-25 10:07:58 +01:00
// Save into tdbb
p.copyTo(tdbb->tdbb_status_vector);
ERR_make_permanent(tdbb->tdbb_status_vector);
2001-05-23 15:26:42 +02:00
}
// Get the BLR version from the CSB stream and complain if it's unknown.
static void getBlrVersion(CompilerScratch* csb)
{
const SSHORT version = csb->csb_blr_reader.getByte();
switch (version)
{
case blr_version4:
csb->blrVersion = 4;
break;
case blr_version5:
csb->blrVersion = 5;
break;
//case blr_version6:
// csb->blrVersion = 6;
// break;
default:
PAR_error(csb, Arg::Gds(isc_metadata_corrupt) <<
Arg::Gds(isc_wroblrver2) << Arg::Num(blr_version4) << Arg::Num(blr_version5/*6*/) <<
Arg::Num(version));
}
}