8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-30 20:03:03 +01:00
firebird-mirror/src/jrd/cmp.cpp

642 lines
16 KiB
C++

/*
* PROGRAM: JRD Access Method
* MODULE: cmp.cpp
* DESCRIPTION: Request compiler
*
* 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.28: John Bellardo: Added code to handle rse_skip.
* 2001.07.17 Claudio Valderrama: Stop crash when parsing user-supplied SQL plan.
* 2001.10.04 Claudio Valderrama: Fix annoying & invalid server complaint about
* triggers not having REFERENCES privilege over their owner table.
* 2002.02.24 Claudio Valderrama: substring() should signal output as string even
* if source is blob and should check implementation limits on field lengths.
* 2002.02.25 Claudio Valderrama: concatenate() should be a civilized function.
* This closes the heart of SF Bug #518282.
* 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.29 Nickolay Samofatov: Added support for savepoints
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
* 2002.10.30 Sean Leyne - Removed support for obsolete "PC_PLATFORM" define
* 2003.10.05 Dmitry Yemanov: Added support for explicit cursors in PSQL
* Adriano dos Santos Fernandes
*/
#include "firebird.h"
#include <string.h>
#include <stdlib.h> // abort
#include "../jrd/jrd.h"
#include "../jrd/req.h"
#include "../jrd/val.h"
#include "../jrd/align.h"
#include "../jrd/lls.h"
#include "../jrd/exe.h"
#include "../jrd/rse.h"
#include "../jrd/scl.h"
#include "../jrd/tra.h"
#include "../jrd/lck.h"
#include "../jrd/irq.h"
#include "../jrd/drq.h"
#include "../jrd/intl.h"
#include "../jrd/btr.h"
#include "../jrd/sort.h"
#include "../common/gdsassert.h"
#include "../jrd/cmp_proto.h"
#include "../common/dsc_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/exe_proto.h"
#include "../jrd/fun_proto.h"
#include "../yvalve/gds_proto.h"
#include "../jrd/idx_proto.h"
#include "../jrd/intl_proto.h"
#include "../jrd/jrd_proto.h"
#include "../jrd/lck_proto.h"
#include "../jrd/opt_proto.h"
#include "../jrd/par_proto.h"
#include "../jrd/met_proto.h"
#include "../jrd/mov_proto.h"
#include "../common/dsc_proto.h"
#include "../jrd/Optimizer.h"
#include "../jrd/DataTypeUtil.h"
#include "../jrd/SysFunction.h"
// Pick up relation ids
#include "../jrd/ini.h"
#include "../common/classes/auto.h"
#include "../common/utils_proto.h"
#include "../dsql/Nodes.h"
#include "../jrd/RecordSourceNodes.h"
#include "../jrd/recsrc/RecordSource.h"
#include "../jrd/recsrc/Cursor.h"
#include "../jrd/Function.h"
#include "../dsql/BoolNodes.h"
#include "../dsql/ExprNodes.h"
#include "../dsql/StmtNodes.h"
using namespace Jrd;
using namespace Firebird;
#ifdef CMP_DEBUG
#include <stdarg.h>
IMPLEMENT_TRACE_ROUTINE(cmp_trace, "CMP")
#endif
// Clone a node.
ValueExprNode* CMP_clone_node(thread_db* tdbb, CompilerScratch* csb, ValueExprNode* node)
{
return NodeCopier::copy(tdbb, csb, node, NULL);
}
// Clone a value node for the optimizer.
// Make a copy of the node (if necessary) and assign impure space.
ValueExprNode* CMP_clone_node_opt(thread_db* tdbb, CompilerScratch* csb, ValueExprNode* node)
{
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
if (node->is<ParameterNode>())
return node;
ValueExprNode* clone = NodeCopier::copy(tdbb, csb, node, NULL);
ExprNode::doPass2(tdbb, csb, &clone);
return clone;
}
BoolExprNode* CMP_clone_node_opt(thread_db* tdbb, CompilerScratch* csb, BoolExprNode* node)
{
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
NodeCopier copier(csb, NULL);
BoolExprNode* clone = copier.copy(tdbb, node);
ExprNode::doPass2(tdbb, csb, &clone);
return clone;
}
jrd_req* CMP_compile2(thread_db* tdbb, const UCHAR* blr, ULONG blr_length, bool internal_flag,
ULONG dbginfo_length, const UCHAR* dbginfo)
{
/**************************************
*
* C M P _ c o m p i l e 2
*
**************************************
*
* Functional description
* Compile a BLR request.
*
**************************************/
jrd_req* request = NULL;
SET_TDBB(tdbb);
Jrd::Attachment* const att = tdbb->getAttachment();
// 26.09.2002 Nickolay Samofatov: default memory pool will become statement pool
// and will be freed by CMP_release
MemoryPool* const new_pool = att->createPool();
try
{
Jrd::ContextPoolHolder context(tdbb, new_pool);
CompilerScratch* csb =
PAR_parse(tdbb, blr, blr_length, internal_flag, dbginfo_length, dbginfo);
request = JrdStatement::makeRequest(tdbb, csb, internal_flag);
new_pool->setStatsGroup(request->req_memory_stats);
#ifdef CMP_DEBUG
if (csb->csb_dump.hasData())
{
csb->dump("streams:\n");
for (StreamType i = 0; i < csb->csb_n_stream; ++i)
{
const CompilerScratch::csb_repeat& s = csb->csb_rpt[i];
csb->dump(
"\t%2d - view_stream: %2d; alias: %s; relation: %s; procedure: %s; view: %s\n",
i, s.csb_view_stream,
(s.csb_alias ? s.csb_alias->c_str() : ""),
(s.csb_relation ? s.csb_relation->rel_name.c_str() : ""),
(s.csb_procedure ? s.csb_procedure->getName().toString().c_str() : ""),
(s.csb_view ? s.csb_view->rel_name.c_str() : ""));
}
cmp_trace("\n%s\n", csb->csb_dump.c_str());
}
#endif
request->getStatement()->verifyAccess(tdbb);
delete csb;
}
catch (const Firebird::Exception& ex)
{
ex.stuffException(tdbb->tdbb_status_vector);
if (request)
CMP_release(tdbb, request);
else
att->deletePool(new_pool);
ERR_punt();
}
return request;
}
CompilerScratch::csb_repeat* CMP_csb_element(CompilerScratch* csb, StreamType element)
{
/**************************************
*
* C M P _ c s b _ e l e m e n t
*
**************************************
*
* Functional description
* Find tail element of compiler scratch block. If the csb isn't big
* enough, extend it.
*
**************************************/
DEV_BLKCHK(csb, type_csb);
CompilerScratch::csb_repeat empty_item;
while (element >= csb->csb_rpt.getCount()) {
csb->csb_rpt.add(empty_item);
}
return &csb->csb_rpt[element];
}
const Format* CMP_format(thread_db* tdbb, CompilerScratch* csb, StreamType stream)
{
/**************************************
*
* C M P _ f o r m a t
*
**************************************
*
* Functional description
* Pick up a format for a stream.
*
**************************************/
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
CompilerScratch::csb_repeat* const tail = &csb->csb_rpt[stream];
if (!tail->csb_format)
{
if (tail->csb_relation)
{
tail->csb_format = MET_current(tdbb, tail->csb_relation);
}
else if (tail->csb_procedure)
{
tail->csb_format = tail->csb_procedure->prc_record_format;
}
else
{
IBERROR(222); // msg 222 bad blr - invalid stream
}
}
fb_assert(tail->csb_format);
return tail->csb_format;
}
IndexLock* CMP_get_index_lock(thread_db* tdbb, jrd_rel* relation, USHORT id)
{
/**************************************
*
* C M P _ g e t _ i n d e x _ l o c k
*
**************************************
*
* Functional description
* Get index lock block for index. If one doesn't exist,
* make one.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
DEV_BLKCHK(relation, type_rel);
if (relation->rel_id < (USHORT) rel_MAX) {
return NULL;
}
// for to find an existing block
for (IndexLock* index = relation->rel_index_locks; index; index = index->idl_next)
{
if (index->idl_id == id) {
return index;
}
}
IndexLock* index = FB_NEW(*relation->rel_pool) IndexLock();
index->idl_next = relation->rel_index_locks;
relation->rel_index_locks = index;
index->idl_relation = relation;
index->idl_id = id;
index->idl_count = 0;
Lock* lock = FB_NEW_RPT(*relation->rel_pool, 0) Lock(tdbb, sizeof(SLONG), LCK_idx_exist);
index->idl_lock = lock;
lock->lck_key.lck_long = (relation->rel_id << 16) | id;
return index;
}
ULONG CMP_impure(CompilerScratch* csb, ULONG size)
{
/**************************************
*
* C M P _ i m p u r e
*
**************************************
*
* Functional description
* Allocate space (offset) in request.
*
**************************************/
DEV_BLKCHK(csb, type_csb);
if (!csb)
{
return 0;
}
const ULONG offset = FB_ALIGN(csb->csb_impure, FB_ALIGNMENT);
if (offset + size > JrdStatement::MAX_REQUEST_SIZE)
{
IBERROR(226); // msg 226: request size limit exceeded
}
csb->csb_impure = offset + size;
return offset;
}
void CMP_post_access(thread_db* tdbb,
CompilerScratch* csb,
const Firebird::MetaName& security_name,
SLONG view_id,
SecurityClass::flags_t mask,
SLONG type_name,
const Firebird::MetaName& name,
const Firebird::MetaName& r_name)
{
/**************************************
*
* C M P _ p o s t _ a c c e s s
*
**************************************
*
* Functional description
* Post access to security class to request.
* We append the new security class to the existing list of
* security classes for that request.
*
**************************************/
DEV_BLKCHK(csb, type_csb);
DEV_BLKCHK(view, type_rel);
// allow all access to internal requests
if (csb->csb_g_flags & (csb_internal | csb_ignore_perm))
return;
SET_TDBB(tdbb);
AccessItem access(security_name, view_id, name, type_name, mask, r_name);
FB_SIZE_T i;
if (csb->csb_access.find(access, i))
{
return;
}
csb->csb_access.insert(i, access);
}
void CMP_post_resource( ResourceList* rsc_ptr, void* obj, Resource::rsc_s type, USHORT id)
{
/**************************************
*
* C M P _ p o s t _ r e s o u r c e
*
**************************************
*
* Functional description
* Post a resource usage to the compiler scratch block.
*
**************************************/
// Initialize resource block
Resource resource(type, id, NULL, NULL, NULL);
switch (type)
{
case Resource::rsc_relation:
case Resource::rsc_index:
resource.rsc_rel = (jrd_rel*) obj;
break;
case Resource::rsc_procedure:
case Resource::rsc_function:
resource.rsc_routine = (Routine*) obj;
break;
case Resource::rsc_collation:
resource.rsc_coll = (Collation*) obj;
break;
default:
BUGCHECK(220); // msg 220 unknown resource
break;
}
// Add it into list if not present already
FB_SIZE_T pos;
if (!rsc_ptr->find(resource, pos))
rsc_ptr->insert(pos, resource);
}
void CMP_release(thread_db* tdbb, jrd_req* request)
{
/**************************************
*
* C M P _ r e l e a s e
*
**************************************
*
* Functional description
* Release a request's statement.
*
**************************************/
DEV_BLKCHK(request, type_req);
request->getStatement()->release(tdbb);
}
StreamType* CMP_alloc_map(thread_db* tdbb, CompilerScratch* csb, StreamType stream)
{
/**************************************
*
* C M P _ a l l o c _ m a p
*
**************************************
*
* Functional description
* Allocate and initialize stream map for view processing.
*
**************************************/
DEV_BLKCHK(csb, type_csb);
SET_TDBB(tdbb);
fb_assert(stream <= MAX_STREAMS);
StreamType* const p = FB_NEW(*tdbb->getDefaultPool()) StreamType[STREAM_MAP_LENGTH];
memset(p, 0, sizeof(StreamType[STREAM_MAP_LENGTH]));
p[0] = stream;
csb->csb_rpt[stream].csb_map = p;
return p;
}
USHORT NodeCopier::getFieldId(const FieldNode* field)
{
return field->fieldId;
}
// Expand dbkey for view.
void CMP_expand_view_nodes(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
ValueExprNodeStack& stack, UCHAR blrOp, bool allStreams)
{
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
// if the stream's dbkey should be ignored, do so
if (!allStreams && (csb->csb_rpt[stream].csb_flags & csb_no_dbkey))
return;
// if the stream references a view, follow map
const StreamType* map = csb->csb_rpt[stream].csb_map;
if (map)
{
++map;
while (*map)
CMP_expand_view_nodes(tdbb, csb, *map++, stack, blrOp, allStreams);
return;
}
// relation is primitive - make dbkey node
if (allStreams || csb->csb_rpt[stream].csb_relation)
{
RecordKeyNode* node = FB_NEW(csb->csb_pool) RecordKeyNode(csb->csb_pool, blrOp);
node->recStream = stream;
stack.push(node);
}
}
// Look at all RseNode's which are lower in scope than the RseNode which this field is
// referencing, and mark them as variant - the rule is that if a field from one RseNode is
// referenced within the scope of another RseNode, the inner RseNode can't be invariant.
// This won't optimize all cases, but it is the simplest operating assumption for now.
void CMP_mark_variant(CompilerScratch* csb, StreamType stream)
{
if (csb->csb_current_nodes.isEmpty())
return;
for (RseOrExprNode* node = csb->csb_current_nodes.end() - 1;
node != csb->csb_current_nodes.begin(); --node)
{
if (node->rseNode)
{
if (node->rseNode->containsStream(stream))
break;
node->rseNode->flags |= RseNode::FLAG_VARIANT;
}
else if (node->exprNode)
node->exprNode->nodFlags &= ~ExprNode::FLAG_INVARIANT;
}
}
// Copy items' information into appropriate node.
ItemInfo* CMP_pass2_validation(thread_db* tdbb, CompilerScratch* csb, const Item& item)
{
ItemInfo itemInfo;
return csb->csb_map_item_info.get(item, itemInfo) ?
FB_NEW(*tdbb->getDefaultPool()) ItemInfo(*tdbb->getDefaultPool(), itemInfo) : NULL;
}
void CMP_post_procedure_access(thread_db* tdbb, CompilerScratch* csb, jrd_prc* procedure)
{
/**************************************
*
* C M P _ p o s t _ p r o c e d u r e _ a c c e s s
*
**************************************
*
* Functional description
*
* The request will inherit access requirements to all the objects
* the called stored procedure has access requirements for.
*
**************************************/
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
// allow all access to internal requests
if (csb->csb_g_flags & (csb_internal | csb_ignore_perm))
return;
// this request must have EXECUTE permission on the stored procedure
if (procedure->getName().package.isEmpty())
{
CMP_post_access(tdbb, csb, procedure->getSecurityName(),
(csb->csb_view ? csb->csb_view->rel_id : 0),
SCL_execute, SCL_object_procedure, procedure->getName().identifier);
}
else
{
CMP_post_access(tdbb, csb, procedure->getSecurityName(),
(csb->csb_view ? csb->csb_view->rel_id : 0),
SCL_execute, SCL_object_package, procedure->getName().package);
}
// Add the procedure to list of external objects accessed
ExternalAccess temp(ExternalAccess::exa_procedure, procedure->getId());
FB_SIZE_T idx;
if (!csb->csb_external.find(temp, idx))
csb->csb_external.insert(idx, temp);
}
RecordSource* CMP_post_rse(thread_db* tdbb, CompilerScratch* csb, RseNode* rse)
{
/**************************************
*
* C M P _ p o s t _ r s e
*
**************************************
*
* Functional description
* Perform actual optimization of an RseNode and clear activity.
*
**************************************/
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
DEV_BLKCHK(rse, type_nod);
RecordSource* rsb = OPT_compile(tdbb, csb, rse, NULL);
if (rse->flags & RseNode::FLAG_SINGULAR)
rsb = FB_NEW(*tdbb->getDefaultPool()) SingularStream(csb, rsb);
if (rse->flags & RseNode::FLAG_WRITELOCK)
{
for (StreamType i = 0; i < csb->csb_n_stream; i++)
csb->csb_rpt[i].csb_flags |= csb_update;
rsb = FB_NEW(*tdbb->getDefaultPool()) LockedStream(csb, rsb);
}
if (rse->flags & RseNode::FLAG_SCROLLABLE)
rsb = FB_NEW(*tdbb->getDefaultPool()) BufferedStream(csb, rsb);
// mark all the substreams as inactive
StreamList streams;
rse->computeRseStreams(streams);
for (StreamList::iterator i = streams.begin(); i != streams.end(); ++i)
csb->csb_rpt[*i].deactivate();
return rsb;
}