2010-02-13 21:29:29 +01:00
|
|
|
/*
|
|
|
|
* 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): ______________________________________.
|
|
|
|
* Adriano dos Santos Fernandes - refactored from pass1.cpp
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef DSQL_VISITORS_H
|
|
|
|
#define DSQL_VISITORS_H
|
|
|
|
|
|
|
|
#include "../dsql/dsql.h"
|
|
|
|
#include "../dsql/node.h"
|
|
|
|
#include "../common/classes/array.h"
|
|
|
|
#include "../common/classes/auto.h"
|
2010-09-20 18:07:50 +02:00
|
|
|
#include "../common/classes/NestConst.h"
|
2010-02-13 21:29:29 +01:00
|
|
|
|
|
|
|
namespace Jrd {
|
|
|
|
|
|
|
|
class CompilerScratch;
|
|
|
|
class dsql_nod;
|
|
|
|
class ExprNode;
|
2010-11-14 18:25:48 +01:00
|
|
|
class FieldNode;
|
2010-08-29 20:20:44 +02:00
|
|
|
class MapNode;
|
2010-12-04 23:15:03 +01:00
|
|
|
class MessageNode;
|
2010-02-13 21:29:29 +01:00
|
|
|
|
|
|
|
|
|
|
|
enum FieldMatchType
|
|
|
|
{
|
|
|
|
FIELD_MATCH_TYPE_EQUAL = 0,
|
|
|
|
FIELD_MATCH_TYPE_LOWER = 1,
|
|
|
|
FIELD_MATCH_TYPE_LOWER_EQUAL = 2
|
|
|
|
/***
|
|
|
|
,
|
|
|
|
FIELD_MATCH_TYPE_HIGHER = 3,
|
|
|
|
FIELD_MATCH_TYPE_HIGHER_EQUAL = 4
|
|
|
|
***/
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2010-02-17 03:04:15 +01:00
|
|
|
// Base class to call a visitor of an ExprNode.
|
|
|
|
class VisitorCaller
|
2010-02-13 21:29:29 +01:00
|
|
|
{
|
2010-02-17 03:04:15 +01:00
|
|
|
public:
|
|
|
|
virtual bool call(ExprNode* exprNode) = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Implementation class to call a visitor of an ExprNode.
|
|
|
|
template <typename T>
|
|
|
|
class VisitorCallerImpl : public VisitorCaller
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
VisitorCallerImpl(T& aVisitor, bool (ExprNode::*aVisitFunction)(T&))
|
|
|
|
: visitor(aVisitor),
|
|
|
|
visitFunction(aVisitFunction)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool call(ExprNode* exprNode)
|
|
|
|
{
|
|
|
|
return (exprNode->*visitFunction)(visitor);
|
|
|
|
}
|
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
private:
|
2010-02-17 03:04:15 +01:00
|
|
|
T& visitor;
|
|
|
|
bool (ExprNode::*visitFunction)(T&);
|
|
|
|
};
|
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
|
2010-02-17 03:04:15 +01:00
|
|
|
template <typename T, typename T2>
|
|
|
|
class DsqlNodeVisitor
|
|
|
|
{
|
2010-02-13 21:29:29 +01:00
|
|
|
public:
|
2010-02-17 03:04:15 +01:00
|
|
|
template <typename Self>
|
|
|
|
DsqlNodeVisitor(bool aAssertOnOthers, bool aReturnOnOthers, bool (ExprNode::*aVisitFunction)(Self&))
|
2010-02-13 21:29:29 +01:00
|
|
|
: assertOnOthers(aAssertOnOthers),
|
|
|
|
returnOnOthers(aReturnOnOthers),
|
|
|
|
currentNode(NULL)
|
|
|
|
{
|
2010-02-17 03:04:15 +01:00
|
|
|
caller = FB_NEW(*getDefaultMemoryPool()) VisitorCallerImpl<Self>(
|
|
|
|
static_cast<Self&>(*this), aVisitFunction);
|
2010-02-13 21:29:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
2010-02-17 03:04:15 +01:00
|
|
|
T getCurrentNode()
|
2010-02-13 21:29:29 +01:00
|
|
|
{
|
|
|
|
return *currentNode;
|
|
|
|
}
|
|
|
|
|
2010-02-17 03:04:15 +01:00
|
|
|
void replaceNode(T node)
|
2010-02-13 21:29:29 +01:00
|
|
|
{
|
|
|
|
*currentNode = node;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool visit(T2 nodePtr)
|
|
|
|
{
|
|
|
|
Firebird::AutoSetRestore<T2> autoInsideHigherMap(¤tNode, nodePtr);
|
|
|
|
return internalVisit(*nodePtr);
|
|
|
|
}
|
|
|
|
|
2010-02-17 03:11:23 +01:00
|
|
|
bool visitChildren(T node);
|
|
|
|
|
|
|
|
virtual bool internalVisit(T node) = 0;
|
|
|
|
|
|
|
|
private:
|
2010-02-17 03:04:15 +01:00
|
|
|
bool call(ExprNode* exprNode)
|
|
|
|
{
|
|
|
|
return caller->call(exprNode);
|
|
|
|
}
|
|
|
|
|
2010-02-13 21:29:29 +01:00
|
|
|
private:
|
|
|
|
const bool assertOnOthers;
|
|
|
|
const bool returnOnOthers;
|
2010-02-17 03:04:15 +01:00
|
|
|
Firebird::AutoPtr<VisitorCaller> caller;
|
2010-02-13 21:29:29 +01:00
|
|
|
T2 currentNode;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef DsqlNodeVisitor<const Jrd::dsql_nod*, const Jrd::dsql_nod* const*> ConstDsqlNodeVisitor;
|
|
|
|
typedef DsqlNodeVisitor<Jrd::dsql_nod*, Jrd::dsql_nod**> NonConstDsqlNodeVisitor;
|
|
|
|
|
|
|
|
|
|
|
|
// Check for an aggregate expression in an expression. It could be buried in an expression
|
|
|
|
// tree and therefore call itselfs again. The level parameters (currentLevel & deepestLevel)
|
|
|
|
// are used to see how deep we are with passing sub-queries (= scope_level).
|
|
|
|
class AggregateFinder : public ConstDsqlNodeVisitor
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
AggregateFinder(const DsqlCompilerScratch* aDsqlScratch, bool aWindow);
|
|
|
|
|
|
|
|
static bool find(const DsqlCompilerScratch* dsqlScratch, bool window, const dsql_nod* node)
|
|
|
|
{
|
|
|
|
return AggregateFinder(dsqlScratch, window).visit(&node);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool internalVisit(const dsql_nod* node);
|
|
|
|
|
|
|
|
public:
|
|
|
|
const DsqlCompilerScratch* const dsqlScratch;
|
|
|
|
bool window;
|
|
|
|
USHORT currentLevel;
|
|
|
|
USHORT deepestLevel;
|
|
|
|
bool ignoreSubSelects;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Check the fields inside an aggregate and check if the field scope_level meets the specified
|
|
|
|
// conditions.
|
|
|
|
//
|
|
|
|
// The SQL 2008 standard says:
|
|
|
|
// <where clause> ::=
|
|
|
|
// WHERE <search condition>
|
|
|
|
// Syntax Rules
|
|
|
|
// 1) If a <value expression> directly contained in the <search condition> is a
|
|
|
|
// <set function specification>, then the <where clause> shall be contained in a <having clause>
|
|
|
|
// or <select list>, the <set function specification> shall contain a column reference, and
|
|
|
|
// every column reference contained in an aggregated argument of the <set function specification>
|
|
|
|
// shall be an outer reference.
|
|
|
|
// NOTE 160 - outer reference is defined in Subclause 6.7, "<column reference>".
|
|
|
|
// 2) The <search condition> shall not contain a <window function> without an intervening
|
|
|
|
// <query expression>.
|
|
|
|
class Aggregate2Finder : public ConstDsqlNodeVisitor
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Aggregate2Finder(USHORT aCheckScopeLevel, FieldMatchType aMatchType, bool aWindowOnly);
|
|
|
|
|
|
|
|
static bool find(USHORT checkScopeLevel, FieldMatchType matchType, bool windowOnly,
|
|
|
|
const dsql_nod* node)
|
|
|
|
{
|
|
|
|
return Aggregate2Finder(checkScopeLevel, matchType, windowOnly).visit(&node);
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
virtual bool internalVisit(const dsql_nod* node);
|
|
|
|
|
|
|
|
public:
|
|
|
|
const USHORT checkScopeLevel;
|
|
|
|
const FieldMatchType matchType;
|
|
|
|
bool windowOnly;
|
|
|
|
bool currentScopeLevelEqual;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Check the fields inside an aggregate and check if the field scope_level meets the specified
|
|
|
|
// conditions.
|
|
|
|
class FieldFinder : public ConstDsqlNodeVisitor
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
FieldFinder(USHORT aCheckScopeLevel, FieldMatchType aMatchType);
|
|
|
|
|
|
|
|
static bool find(USHORT checkScopeLevel, FieldMatchType matchType, const dsql_nod* node)
|
|
|
|
{
|
|
|
|
return FieldFinder(checkScopeLevel, matchType).visit(&node);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool internalVisit(const dsql_nod* node);
|
|
|
|
|
|
|
|
public:
|
|
|
|
bool getField() const
|
|
|
|
{
|
|
|
|
return field;
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
const USHORT checkScopeLevel;
|
|
|
|
const FieldMatchType matchType;
|
|
|
|
bool field;
|
|
|
|
};
|
|
|
|
|
2010-02-16 09:53:31 +01:00
|
|
|
// Validate that an expanded field / context pair is in a specified list. This is used in one
|
2010-02-13 21:29:29 +01:00
|
|
|
// instance to check that a simple field selected through a grouping rse is a grouping field -
|
|
|
|
// thus a valid field reference. For the sake of argument, we'll match qualified to unqualified
|
|
|
|
// reference, but qualified reference must match completely.
|
|
|
|
// A list element containing a simple CAST for collation purposes is allowed.
|
|
|
|
class InvalidReferenceFinder : public ConstDsqlNodeVisitor
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
InvalidReferenceFinder(const dsql_ctx* aContext, const dsql_nod* aList);
|
|
|
|
|
|
|
|
static bool find(const dsql_ctx* context, const dsql_nod* list, const dsql_nod* node)
|
|
|
|
{
|
|
|
|
return InvalidReferenceFinder(context, list).visit(&node);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool internalVisit(const dsql_nod* node);
|
|
|
|
|
|
|
|
public:
|
|
|
|
const dsql_ctx* const context;
|
|
|
|
const dsql_nod* const list;
|
|
|
|
bool insideOwnMap;
|
|
|
|
bool insideHigherMap;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Called to map fields used in an aggregate-context after all pass1 calls
|
|
|
|
// (SELECT-, ORDER BY-lists). Walk completly through the given node 'field' and map the fields
|
|
|
|
// with same scope_level as the given context to the given context with the post_map function.
|
|
|
|
class FieldRemapper : public NonConstDsqlNodeVisitor
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
FieldRemapper(DsqlCompilerScratch* aDsqlScratch, dsql_ctx* aContext, bool aWindow,
|
|
|
|
dsql_nod* aPartitionNode, dsql_nod* aOrderNode);
|
|
|
|
|
|
|
|
static dsql_nod* remap(DsqlCompilerScratch* dsqlScratch, dsql_ctx* context, bool window,
|
|
|
|
dsql_nod* field, dsql_nod* partitionNode = NULL, dsql_nod* orderNode = NULL)
|
|
|
|
{
|
|
|
|
// The bool value returned by the visitor is completely discarded in this class.
|
|
|
|
FieldRemapper(dsqlScratch, context, window, partitionNode, orderNode).visit(&field);
|
|
|
|
return field;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool internalVisit(dsql_nod* node);
|
|
|
|
|
|
|
|
public:
|
|
|
|
DsqlCompilerScratch* const dsqlScratch;
|
|
|
|
dsql_ctx* const context;
|
|
|
|
const bool window;
|
|
|
|
dsql_nod* partitionNode;
|
|
|
|
dsql_nod* orderNode;
|
|
|
|
USHORT currentLevel;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Search if a sub select is buried inside a select list from a query expression.
|
|
|
|
class SubSelectFinder : public ConstDsqlNodeVisitor
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
SubSelectFinder();
|
|
|
|
|
|
|
|
static bool find(const dsql_nod* node)
|
|
|
|
{
|
|
|
|
return SubSelectFinder().visit(&node);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool internalVisit(const dsql_nod* node);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
// Generic node copier.
|
|
|
|
class NodeCopier
|
2010-02-13 21:29:29 +01:00
|
|
|
{
|
|
|
|
public:
|
2012-04-12 11:02:13 +02:00
|
|
|
NodeCopier(CompilerScratch* aCsb, StreamType* aRemap)
|
2010-11-21 04:47:29 +01:00
|
|
|
: csb(aCsb),
|
|
|
|
remap(aRemap),
|
|
|
|
message(NULL)
|
2010-02-17 03:04:15 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
virtual ~NodeCopier()
|
2010-02-13 21:29:29 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
2010-11-21 04:47:29 +01:00
|
|
|
// Copy an expression tree remapping field streams. If the map isn't present, don't remap.
|
|
|
|
template <typename T>
|
2011-02-06 22:59:20 +01:00
|
|
|
T* copy(thread_db* tdbb, const T* input)
|
2010-02-13 21:29:29 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
if (!input)
|
|
|
|
return NULL;
|
2010-02-13 21:29:29 +01:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
T* copy = input->copy(tdbb, *this);
|
2010-12-04 23:15:03 +01:00
|
|
|
postCopy(input, copy, copy);
|
2010-02-13 21:29:29 +01:00
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
return copy;
|
2010-02-13 21:29:29 +01:00
|
|
|
}
|
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
template <typename T>
|
2011-02-06 22:59:20 +01:00
|
|
|
T* copy(thread_db* tdbb, const NestConst<T>& input)
|
2010-02-13 21:29:29 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
return copy(tdbb, input.getObject());
|
2010-02-13 21:29:29 +01:00
|
|
|
}
|
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
template <typename T>
|
2012-04-12 11:02:13 +02:00
|
|
|
static T* copy(thread_db* tdbb, CompilerScratch* csb, const T* input, StreamType* remap)
|
2010-02-13 21:29:29 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
return NodeCopier(csb, remap).copy(tdbb, input);
|
2010-02-13 21:29:29 +01:00
|
|
|
}
|
|
|
|
|
2010-11-21 04:47:29 +01:00
|
|
|
template <typename T>
|
2012-04-12 11:02:13 +02:00
|
|
|
static T* copy(thread_db* tdbb, CompilerScratch* csb, const NestConst<T>& input, StreamType* remap)
|
2010-02-13 21:29:29 +01:00
|
|
|
{
|
2010-11-21 04:47:29 +01:00
|
|
|
return NodeCopier(csb, remap).copy(tdbb, input.getObject());
|
2010-02-13 21:29:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual USHORT remapField(USHORT /*stream*/, USHORT fldId)
|
|
|
|
{
|
|
|
|
return fldId;
|
|
|
|
}
|
|
|
|
|
2011-02-06 22:59:20 +01:00
|
|
|
virtual USHORT getFieldId(const FieldNode* input);
|
2010-02-13 21:29:29 +01:00
|
|
|
|
2010-12-04 23:15:03 +01:00
|
|
|
private:
|
2011-02-06 22:59:20 +01:00
|
|
|
template <typename T1, typename T2> void postCopy(const T1* source, T2* target, ExprNode* /*dummy*/)
|
2010-12-04 23:15:03 +01:00
|
|
|
{
|
|
|
|
target->nodFlags = source->nodFlags;
|
|
|
|
}
|
|
|
|
|
2011-02-06 22:59:20 +01:00
|
|
|
void postCopy(const StmtNode* /*source*/, StmtNode* /*target*/, StmtNode* /*dummy*/)
|
2010-12-04 23:15:03 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2010-08-24 05:25:01 +02:00
|
|
|
public:
|
2012-04-12 11:02:13 +02:00
|
|
|
CompilerScratch* const csb;
|
|
|
|
StreamType* const remap;
|
2010-12-04 23:15:03 +01:00
|
|
|
MessageNode* message;
|
2010-02-13 21:29:29 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template <typename T, typename T2>
|
|
|
|
inline bool DsqlNodeVisitor<T, T2>::visitChildren(T node)
|
|
|
|
{
|
|
|
|
using namespace Firebird;
|
|
|
|
using namespace Dsql;
|
|
|
|
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
if (!node)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
switch (node->nod_type)
|
|
|
|
{
|
|
|
|
case nod_class_exprnode:
|
|
|
|
{
|
|
|
|
Jrd::ExprNode* exprNode = reinterpret_cast<Jrd::ExprNode*>(node->nod_arg[0]);
|
2010-02-17 03:04:15 +01:00
|
|
|
ret |= call(exprNode);
|
2010-02-13 21:29:29 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case nod_list:
|
|
|
|
{
|
|
|
|
T2 ptr = node->nod_arg;
|
|
|
|
for (T2 end = ptr + node->nod_count; ptr < end; ++ptr)
|
|
|
|
ret |= visit(ptr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2010-02-22 17:00:49 +01:00
|
|
|
fb_assert(!assertOnOthers);
|
2010-02-13 21:29:29 +01:00
|
|
|
return returnOnOthers;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
#endif // DSQL_VISITORS_H
|