mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-23 06:03:02 +01:00
Fixed problems with views WITH CHECK OPTION. Also change its triggers as asked for opinions in fb-devel.
This commit is contained in:
parent
844b15bd13
commit
5580857d73
@ -7960,23 +7960,11 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
|
||||
Arg::Gds(isc_col_name_err));
|
||||
}
|
||||
|
||||
RecordSourceNode* querySpecNod = selectExpr->querySpec;
|
||||
|
||||
#if 0 //// FIXME:
|
||||
if (querySpecNod->nod_type == Dsql::nod_list)
|
||||
{
|
||||
// Only one table allowed for VIEW WITH CHECK OPTION
|
||||
status_exception::raise(
|
||||
Arg::Gds(isc_sqlerr) << Arg::Num(-607) <<
|
||||
Arg::Gds(isc_dsql_command_err) <<
|
||||
Arg::Gds(isc_table_view_err));
|
||||
}
|
||||
#endif
|
||||
|
||||
RseNode* querySpec = ExprNode::as<RseNode>(querySpecNod);
|
||||
RseNode* querySpec = selectExpr->querySpec->as<RseNode>();
|
||||
fb_assert(querySpec);
|
||||
|
||||
if (querySpec->dsqlFrom->items.getCount() != 1)
|
||||
if (querySpec->dsqlFrom->items.getCount() != 1 ||
|
||||
!querySpec->dsqlFrom->items[0]->is<ProcedureSourceNode>())
|
||||
{
|
||||
// Only one table allowed for VIEW WITH CHECK OPTION
|
||||
status_exception::raise(
|
||||
@ -8003,7 +7991,7 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
|
||||
Arg::Gds(isc_distinct_err));
|
||||
}
|
||||
|
||||
createCheckTriggers(tdbb, dsqlScratch, items);
|
||||
createCheckTrigger(tdbb, dsqlScratch, items);
|
||||
}
|
||||
|
||||
DDL_reset_context_stack(dsqlScratch);
|
||||
@ -8017,46 +8005,21 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
|
||||
MET_dsql_cache_release(tdbb, SYM_relation, name);
|
||||
}
|
||||
|
||||
// Generate triggers to implement the WITH CHECK OPTION clause for a VIEW.
|
||||
void CreateAlterViewNode::createCheckTriggers(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
|
||||
// Generate a trigger to implement the WITH CHECK OPTION clause for a VIEW.
|
||||
void CreateAlterViewNode::createCheckTrigger(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
|
||||
ValueListNode* items)
|
||||
{
|
||||
MemoryPool& pool = *tdbb->getDefaultPool();
|
||||
|
||||
// Specify that the trigger should abort if the condition is not met.
|
||||
CompoundStmtNode* actionNode = FB_NEW(pool) CompoundStmtNode(pool);
|
||||
|
||||
ExceptionNode* exceptionNode = FB_NEW(pool) ExceptionNode(pool, CHECK_CONSTRAINT_EXCEPTION);
|
||||
exceptionNode->exception->type = ExceptionItem::GDS_CODE;
|
||||
|
||||
actionNode->statements.add(exceptionNode);
|
||||
|
||||
// Create the UPDATE trigger.
|
||||
|
||||
BoolExprNode* baseAndNode = NULL;
|
||||
RelationSourceNode* baseRelation = NULL;
|
||||
defineUpdateAction(dsqlScratch, &baseAndNode, &baseRelation, items);
|
||||
fb_assert(baseAndNode);
|
||||
fb_assert(baseRelation);
|
||||
|
||||
RseNode* rse = FB_NEW(pool) RseNode(pool);
|
||||
rse->dsqlWhere = baseAndNode;
|
||||
rse->dsqlStreams = FB_NEW(pool) RecSourceListNode(pool, 1);
|
||||
rse->dsqlStreams->items[0] = baseRelation;
|
||||
|
||||
createCheckTrigger(tdbb, dsqlScratch, rse, items, actionNode, PRE_MODIFY_TRIGGER);
|
||||
createCheckTrigger(tdbb, dsqlScratch, NULL, items, actionNode, PRE_STORE_TRIGGER);
|
||||
}
|
||||
|
||||
// Define a trigger for a VIEW WITH CHECK OPTION.
|
||||
void CreateAlterViewNode::createCheckTrigger(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
|
||||
RseNode* rse, ValueListNode* items, CompoundStmtNode* actions, TriggerType triggerType)
|
||||
{
|
||||
MemoryPool& pool = *tdbb->getDefaultPool();
|
||||
// Create the trigger - PRE_MODIFY_TRIGGER | PRE_STORE_TRIGGER. See parse.y/trigger_type_suffix.
|
||||
TriggerType triggerType = TriggerType(((1 << 1) | (2 << 3)) - 1);
|
||||
|
||||
AutoSetRestore<bool> autoCheckConstraintTrigger(&dsqlScratch->checkConstraintTrigger, true);
|
||||
|
||||
RecordSourceNode* querySpecNod = selectExpr->querySpec;
|
||||
RelationSourceNode* relationNode = dsqlNode;
|
||||
|
||||
// Generate the trigger blr.
|
||||
@ -8067,82 +8030,83 @@ void CreateAlterViewNode::createCheckTrigger(thread_db* tdbb, DsqlCompilerScratc
|
||||
|
||||
dsqlScratch->appendUChar(blr_begin);
|
||||
|
||||
// Create the "OLD" and "NEW" contexts for the trigger -- the new one could be a dummy place
|
||||
// holder to avoid resolving fields to that context but prevent relations referenced in
|
||||
// the trigger actions from referencing the predefined "1" context.
|
||||
|
||||
dsql_ctx* savContext = NULL;
|
||||
dsql_ctx* context = NULL;
|
||||
|
||||
if (dsqlScratch->contextNumber)
|
||||
{
|
||||
// If an alias is specified for the single base table involved,
|
||||
// save and then add the context.
|
||||
|
||||
context = dsqlScratch->context->object();
|
||||
|
||||
if (context->ctx_alias.hasData())
|
||||
{
|
||||
savContext = FB_NEW(pool) dsql_ctx(pool);
|
||||
*savContext = *context;
|
||||
}
|
||||
}
|
||||
|
||||
DDL_reset_context_stack(dsqlScratch);
|
||||
|
||||
string tempAlias = relationNode->alias;
|
||||
++dsqlScratch->contextNumber; // OLD context
|
||||
|
||||
relationNode->alias = OLD_CONTEXT;
|
||||
dsql_ctx* oldContext = PASS1_make_context(dsqlScratch, relationNode);
|
||||
oldContext->ctx_flags |= CTX_system;
|
||||
|
||||
relationNode->alias = NEW_CONTEXT;
|
||||
dsql_ctx* newContext = PASS1_make_context(dsqlScratch, relationNode);
|
||||
newContext->ctx_flags |= CTX_system;
|
||||
|
||||
relationNode->alias = tempAlias;
|
||||
|
||||
if (savContext)
|
||||
{
|
||||
savContext->ctx_context = dsqlScratch->contextNumber++;
|
||||
context->ctx_scope_level = dsqlScratch->scopeLevel;
|
||||
dsqlScratch->context->push(savContext);
|
||||
}
|
||||
|
||||
// Generate the condition for firing the trigger.
|
||||
|
||||
RseNode* querySpec = querySpecNod->as<RseNode>();
|
||||
RseNode* querySpec = selectExpr->querySpec->as<RseNode>();
|
||||
fb_assert(querySpec);
|
||||
|
||||
if (triggerType == PRE_MODIFY_TRIGGER)
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_for);
|
||||
// ASF: We'll now map the view's base table into the trigger's NEW context.
|
||||
|
||||
rse->dsqlStreams->items[0] = doDsqlPass(dsqlScratch, rse->dsqlStreams->items[0]);
|
||||
rse->dsqlWhere = doDsqlPass(dsqlScratch, rse->dsqlWhere);
|
||||
dsql_ctx* newContext;
|
||||
|
||||
GEN_expr(dsqlScratch, rse);
|
||||
{ /// scope
|
||||
ProcedureSourceNode* sourceNode = querySpec->dsqlFrom->items[0]->as<ProcedureSourceNode>();
|
||||
AutoSetRestore<string> autoAlias(&relationNode->alias, sourceNode->alias);
|
||||
|
||||
replaceFieldNames(querySpec->dsqlWhere.getObject(), items, viewFields, false, NEW_CONTEXT);
|
||||
if (relationNode->alias.isEmpty())
|
||||
relationNode->alias = sourceNode->dsqlName.identifier.c_str();
|
||||
|
||||
newContext = PASS1_make_context(dsqlScratch, relationNode);
|
||||
newContext->ctx_flags |= CTX_system | CTX_view_with_check;
|
||||
}
|
||||
else if (triggerType == PRE_STORE_TRIGGER)
|
||||
replaceFieldNames(querySpec->dsqlWhere.getObject(), items, viewFields, true, NEW_CONTEXT);
|
||||
else
|
||||
fb_assert(false);
|
||||
|
||||
NestConst<BoolExprNode> condition = querySpec->dsqlWhere;
|
||||
// Replace the view's field names by the base table field names. Save the original names
|
||||
// to restore after the condition processing.
|
||||
|
||||
dsql_fld* field = newContext->ctx_relation->rel_fields;
|
||||
ObjectsArray<string> savedNames;
|
||||
|
||||
// ASF: rel_fields entries are in reverse order.
|
||||
for (NestConst<ValueExprNode>* ptr = items->items.end();
|
||||
ptr-- != items->items.begin();
|
||||
field = field->fld_next)
|
||||
{
|
||||
ValueExprNode* valueNode = *ptr;
|
||||
DsqlAliasNode* aliasNode;
|
||||
|
||||
if ((aliasNode = valueNode->as<DsqlAliasNode>()))
|
||||
valueNode = aliasNode->value;
|
||||
|
||||
FieldNode* fieldNode = valueNode->as<FieldNode>();
|
||||
fb_assert(fieldNode);
|
||||
|
||||
savedNames.add(field->fld_name);
|
||||
|
||||
dsql_fld* queryField = fieldNode->dsqlField;
|
||||
|
||||
field->fld_name = queryField->fld_name;
|
||||
field->fld_dtype = queryField->fld_dtype;
|
||||
field->fld_scale = queryField->fld_scale;
|
||||
field->fld_sub_type = queryField->fld_sub_type;
|
||||
field->fld_length = queryField->fld_length;
|
||||
field->fld_flags = queryField->fld_flags;
|
||||
field->fld_character_set_id = queryField->fld_character_set_id;
|
||||
field->fld_collation_id = queryField->fld_collation_id;
|
||||
}
|
||||
|
||||
dsqlScratch->appendUChar(blr_if);
|
||||
GEN_expr(dsqlScratch, doDsqlPass(dsqlScratch, condition));
|
||||
|
||||
// Process the condition for firing the trigger.
|
||||
NestConst<BoolExprNode> condition = doDsqlPass(dsqlScratch, querySpec->dsqlWhere);
|
||||
|
||||
// Restore the field names. This must happen before the condition's BLR generation.
|
||||
|
||||
field = newContext->ctx_relation->rel_fields;
|
||||
|
||||
for (ObjectsArray<string>::iterator i = savedNames.begin(); i != savedNames.end(); ++i)
|
||||
{
|
||||
field->fld_name = *i;
|
||||
field = field->fld_next;
|
||||
}
|
||||
|
||||
GEN_expr(dsqlScratch, condition);
|
||||
dsqlScratch->appendUChar(blr_begin);
|
||||
dsqlScratch->appendUChar(blr_end);
|
||||
|
||||
// Generate the action statements for the trigger.
|
||||
|
||||
NestConst<StmtNode>* ptr = actions->statements.begin();
|
||||
|
||||
for (const NestConst<StmtNode>* const end = actions->statements.end(); ptr != end; ++ptr)
|
||||
(*ptr)->dsqlPass(dsqlScratch)->genBlr(dsqlScratch);
|
||||
// Generate the action statement for the trigger.
|
||||
exceptionNode->dsqlPass(dsqlScratch)->genBlr(dsqlScratch);
|
||||
|
||||
dsqlScratch->appendUChar(blr_end); // of begin
|
||||
dsqlScratch->appendUChar(blr_eoc);
|
||||
@ -8157,211 +8121,6 @@ void CreateAlterViewNode::createCheckTrigger(thread_db* tdbb, DsqlCompilerScratc
|
||||
trigger.store(tdbb, dsqlScratch, dsqlScratch->getTransaction());
|
||||
}
|
||||
|
||||
// Define an action statement which, given a view definition, will map an update to a record from
|
||||
// a view of a single relation into the base relation.
|
||||
void CreateAlterViewNode::defineUpdateAction(DsqlCompilerScratch* dsqlScratch,
|
||||
BoolExprNode** baseAndNode, RelationSourceNode** baseRelation, ValueListNode* items)
|
||||
{
|
||||
thread_db* tdbb = JRD_get_thread_data();
|
||||
MemoryPool& pool = *tdbb->getDefaultPool();
|
||||
|
||||
// Check whether this is an updatable view definition.
|
||||
|
||||
RseNode* querySpec = selectExpr->querySpec->as<RseNode>();
|
||||
RecSourceListNode* fromList = NULL;
|
||||
|
||||
if (!querySpec || !(fromList = querySpec->dsqlFrom) || fromList->items.getCount() != 1)
|
||||
{
|
||||
// The caller seems throwing proper errors for all the above conditions.
|
||||
// But just in case it doesn't, here we have the final attempt to prevent the bad things.
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
// Use the relation referenced in the select statement for rse.
|
||||
|
||||
RelationSourceNode* relationNode = FB_NEW(pool) RelationSourceNode(pool,
|
||||
fromList->items[0]->as<ProcedureSourceNode>()->dsqlName.identifier);
|
||||
relationNode->alias = TEMP_CONTEXT;
|
||||
|
||||
*baseRelation = relationNode;
|
||||
|
||||
// Get the list of values and fields to compare to -- if there is no list of fields, get all
|
||||
// fields in the base relation that are not computed.
|
||||
|
||||
ValueListNode* valuesNode = viewFields;
|
||||
ValueListNode* fieldsNode = querySpec->dsqlSelectList;
|
||||
|
||||
if (!fieldsNode)
|
||||
{
|
||||
const dsql_rel* relation = METD_get_relation(dsqlScratch->getTransaction(),
|
||||
dsqlScratch, name);
|
||||
fieldsNode = FB_NEW(pool) ValueListNode(pool, 0u);
|
||||
|
||||
for (const dsql_fld* field = relation->rel_fields; field; field = field->fld_next)
|
||||
{
|
||||
if (!(field->fld_flags & FLD_computed))
|
||||
fieldsNode->add(MAKE_field_name(field->fld_name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!valuesNode)
|
||||
valuesNode = fieldsNode;
|
||||
|
||||
// Generate the list of assignments to fields in the base relation.
|
||||
|
||||
NestConst<ValueExprNode>* ptr = fieldsNode->items.begin();
|
||||
const NestConst<ValueExprNode>* const end = fieldsNode->items.end();
|
||||
NestConst<ValueExprNode>* ptr2 = valuesNode->items.begin();
|
||||
const NestConst<ValueExprNode>* const end2 = valuesNode->items.end();
|
||||
int andArg = 0;
|
||||
|
||||
BinaryBoolNode* andNode = FB_NEW(pool) BinaryBoolNode(pool, blr_and);
|
||||
|
||||
for (; (ptr < end) && (ptr2 < end2); ptr++, ptr2++)
|
||||
{
|
||||
ValueExprNode* fieldNod = *ptr;
|
||||
DsqlAliasNode* aliasNode;
|
||||
|
||||
if ((aliasNode = ExprNode::as<DsqlAliasNode>(fieldNod)))
|
||||
fieldNod = aliasNode->value;
|
||||
|
||||
FieldNode* fieldNode = ExprNode::as<FieldNode>(fieldNod);
|
||||
|
||||
// Generate the actual comparisons.
|
||||
|
||||
if (fieldNode)
|
||||
{
|
||||
fieldNode->dsqlQualifier = TEMP_CONTEXT;
|
||||
|
||||
FieldNode* oldValueNode = FB_NEW(pool) FieldNode(pool);
|
||||
oldValueNode->dsqlName = (*ptr2)->as<FieldNode>()->dsqlName;
|
||||
oldValueNode->dsqlQualifier = OLD_CONTEXT;
|
||||
|
||||
ComparativeBoolNode* eqlNode = FB_NEW(pool) ComparativeBoolNode(pool,
|
||||
blr_eql, oldValueNode, fieldNod);
|
||||
|
||||
BinaryBoolNode* andNode2 = FB_NEW(pool) BinaryBoolNode(pool, blr_and,
|
||||
FB_NEW(pool) MissingBoolNode(pool, oldValueNode),
|
||||
FB_NEW(pool) MissingBoolNode(pool, fieldNod));
|
||||
|
||||
BinaryBoolNode* orNode = FB_NEW(pool) BinaryBoolNode(pool, blr_or, eqlNode, andNode2);
|
||||
|
||||
if (andArg == 0)
|
||||
{
|
||||
++andArg;
|
||||
andNode->arg1 = orNode;
|
||||
}
|
||||
else if (andArg == 1)
|
||||
{
|
||||
++andArg;
|
||||
andNode->arg2 = orNode;
|
||||
}
|
||||
else
|
||||
andNode = FB_NEW(pool) BinaryBoolNode(pool, blr_and, andNode, orNode);
|
||||
}
|
||||
}
|
||||
|
||||
if (andArg == 0)
|
||||
{
|
||||
andNode->arg1 = querySpec->dsqlWhere;
|
||||
replaceFieldNames(andNode->arg1.getObject(), items, NULL, false, TEMP_CONTEXT);
|
||||
}
|
||||
else if (andArg == 1)
|
||||
{
|
||||
andNode->arg2 = querySpec->dsqlWhere;
|
||||
replaceFieldNames(andNode->arg2.getObject(), items, NULL, false, TEMP_CONTEXT);
|
||||
}
|
||||
else
|
||||
{
|
||||
replaceFieldNames(querySpec->dsqlWhere.getObject(), items, NULL, false, TEMP_CONTEXT);
|
||||
andNode = FB_NEW(pool) BinaryBoolNode(pool, blr_and, andNode, querySpec->dsqlWhere);
|
||||
}
|
||||
|
||||
*baseAndNode = andNode;
|
||||
}
|
||||
|
||||
// Given an input node tree, find any field name nodes and replace them according to the mapping
|
||||
// provided. This is used to create view WITH CHECK OPTION.
|
||||
void CreateAlterViewNode::replaceFieldNames(ExprNode* input, ValueListNode* searchFields,
|
||||
ValueListNode* replaceFields, bool nullThem, const char* contextName)
|
||||
{
|
||||
thread_db* tdbb = JRD_get_thread_data();
|
||||
|
||||
if (!input)
|
||||
return;
|
||||
|
||||
Array<ExprNode*> temp;
|
||||
|
||||
for (NodeRef** i = input->dsqlChildNodes.begin(); i != input->dsqlChildNodes.end(); ++i)
|
||||
{
|
||||
if (**i)
|
||||
temp.add((*i)->getExpr());
|
||||
}
|
||||
|
||||
for (ExprNode** ptr = temp.begin(); ptr != temp.end(); ++ptr)
|
||||
{
|
||||
ExprNode*& ptrNode = *ptr;
|
||||
|
||||
if (!ptrNode)
|
||||
continue;
|
||||
|
||||
if (ptrNode->as<SelectExprNode>())
|
||||
{
|
||||
// No subqueries permitted for VIEW WITH CHECK OPTION
|
||||
status_exception::raise(
|
||||
Arg::Gds(isc_sqlerr) << Arg::Num(-607) <<
|
||||
Arg::Gds(isc_dsql_command_err) <<
|
||||
Arg::Gds(isc_subquery_err));
|
||||
}
|
||||
|
||||
FieldNode* fieldNode = ptrNode->as<FieldNode>();
|
||||
|
||||
if (fieldNode)
|
||||
{
|
||||
// Found a field node, check if it needs to be replaced.
|
||||
|
||||
NestConst<ValueExprNode>* search = searchFields->items.begin();
|
||||
const NestConst<ValueExprNode>* const end = searchFields->items.end();
|
||||
NestConst<ValueExprNode>* replace = NULL;
|
||||
|
||||
if (replaceFields)
|
||||
replace = replaceFields->items.begin();
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (; search < end; ++search, replace += (replaceFields ? 1 : 0))
|
||||
{
|
||||
FieldNode* replaceField = replace ? (*replace)->as<FieldNode>() : NULL;
|
||||
|
||||
MetaName replaceName(replaceFields ? replaceField->dsqlName : "");
|
||||
|
||||
const dsql_fld* field = (*search)->as<FieldNode>()->dsqlField;
|
||||
|
||||
if (field->fld_name == fieldNode->dsqlName.c_str())
|
||||
{
|
||||
found = true;
|
||||
|
||||
if (replaceFields)
|
||||
fieldNode->dsqlName = replaceField->dsqlName;
|
||||
|
||||
fieldNode->dsqlQualifier = contextName;
|
||||
}
|
||||
|
||||
if (nullThem && replaceFields && fieldNode->dsqlName == replaceName)
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (nullThem && !found)
|
||||
ptrNode = FB_NEW(*tdbb->getDefaultPool()) NullNode(*tdbb->getDefaultPool());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Recursively go through the input tree looking for field name nodes.
|
||||
replaceFieldNames(ptrNode, searchFields, replaceFields, nullThem, contextName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------
|
||||
|
||||
|
@ -1447,13 +1447,7 @@ protected:
|
||||
}
|
||||
|
||||
private:
|
||||
void createCheckTriggers(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, ValueListNode* items);
|
||||
void createCheckTrigger(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
|
||||
RseNode* rse, ValueListNode* items, CompoundStmtNode* actions, TriggerType triggerType);
|
||||
void defineUpdateAction(DsqlCompilerScratch* dsqlScratch, BoolExprNode** baseAndNode,
|
||||
RelationSourceNode** baseRelation, ValueListNode* items);
|
||||
static void replaceFieldNames(ExprNode* input, ValueListNode* searchFields,
|
||||
ValueListNode* replaceFields, bool nullThem, const char* contextName);
|
||||
void createCheckTrigger(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, ValueListNode* items);
|
||||
|
||||
public:
|
||||
bool create;
|
||||
|
@ -4991,7 +4991,13 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec
|
||||
}
|
||||
}
|
||||
|
||||
if (dsqlQualifier.hasData() && !field)
|
||||
if ((context->ctx_flags & CTX_view_with_check) && !field)
|
||||
{
|
||||
node = FB_NEW(*tdbb->getDefaultPool()) NullNode(*tdbb->getDefaultPool());
|
||||
node->line = line;
|
||||
node->column = column;
|
||||
}
|
||||
else if (dsqlQualifier.hasData() && !field)
|
||||
{
|
||||
// If a qualifier was present and we didn't find
|
||||
// a matching field then we should stop searching.
|
||||
@ -4999,8 +5005,7 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (field || usingField)
|
||||
else if (field || usingField)
|
||||
{
|
||||
// Intercept any reference to a field with datatype that
|
||||
// did not exist prior to V6 and post an error
|
||||
@ -9512,7 +9517,7 @@ void SubstringNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
||||
dsqlScratch->appendUChar(blr_literal);
|
||||
dsqlScratch->appendUChar(blr_long);
|
||||
dsqlScratch->appendUChar(0);
|
||||
dsqlScratch->appendUShort(LONG_POS_MAX & 0xffff); // avoid warning
|
||||
dsqlScratch->appendUShort(LONG_POS_MAX & 0xFFFF);
|
||||
dsqlScratch->appendUShort(LONG_POS_MAX >> 16);
|
||||
}
|
||||
}
|
||||
|
@ -62,9 +62,8 @@ DEFINE_TRACE_ROUTINE(dsql_trace);
|
||||
#include "../jrd/EngineInterface.h"
|
||||
|
||||
// Context aliases used in triggers
|
||||
const char* const OLD_CONTEXT = "OLD";
|
||||
const char* const NEW_CONTEXT = "NEW";
|
||||
const char* const TEMP_CONTEXT = "TEMP";
|
||||
const char* const OLD_CONTEXT = "OLD";
|
||||
const char* const NEW_CONTEXT = "NEW";
|
||||
|
||||
namespace Jrd
|
||||
{
|
||||
@ -745,11 +744,12 @@ public:
|
||||
|
||||
// Flag values for ctx_flags
|
||||
|
||||
const USHORT CTX_outer_join = 0x01; // reference is part of an outer join
|
||||
const USHORT CTX_system = 0x02; // Context generated by system (NEW/OLD in triggers, check-constraint, RETURNING)
|
||||
const USHORT CTX_null = 0x04; // Fields of the context should be resolved to NULL constant
|
||||
const USHORT CTX_returning = 0x08; // Context generated by RETURNING
|
||||
const USHORT CTX_recursive = 0x10; // Context has secondary number (ctx_recursive) generated for recursive UNION
|
||||
const USHORT CTX_outer_join = 0x01; // reference is part of an outer join
|
||||
const USHORT CTX_system = 0x02; // Context generated by system (NEW/OLD in triggers, check-constraint, RETURNING)
|
||||
const USHORT CTX_null = 0x04; // Fields of the context should be resolved to NULL constant
|
||||
const USHORT CTX_returning = 0x08; // Context generated by RETURNING
|
||||
const USHORT CTX_recursive = 0x10; // Context has secondary number (ctx_recursive) generated for recursive UNION
|
||||
const USHORT CTX_view_with_check = 0x20; // Context of WITH CHECK OPTION view's triggers
|
||||
|
||||
//! Aggregate/union map block to map virtual fields to their base
|
||||
//! TMN: NOTE! This datatype should definitely be renamed!
|
||||
|
Loading…
Reference in New Issue
Block a user