mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-27 17:23:03 +01:00
1151 lines
30 KiB
Plaintext
1151 lines
30 KiB
Plaintext
/*
|
|
* The contents of this file are subject to the Initial
|
|
* Developer's 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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
|
|
*
|
|
* Software distributed under the License is distributed AS IS,
|
|
* 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 Adriano dos Santos Fernandes
|
|
* for the Firebird Open Source RDBMS project.
|
|
*
|
|
* Copyright (c) 2009 Adriano dos Santos Fernandes <adrianosf@uol.com.br>
|
|
* and all contributors signed below.
|
|
*
|
|
* All Rights Reserved.
|
|
* Contributor(s): ______________________________________.
|
|
*/
|
|
|
|
#include "firebird.h"
|
|
#include "../jrd/common.h"
|
|
#include "../dsql/PackageNodes.h"
|
|
#include "../jrd/dyn.h"
|
|
#include "../jrd/intl.h"
|
|
#include "../jrd/jrd.h"
|
|
#include "../jrd/tra.h"
|
|
#include "../jrd/dfw_proto.h"
|
|
#include "../jrd/dyn_dl_proto.h"
|
|
#include "../jrd/exe_proto.h"
|
|
#include "../jrd/met_proto.h"
|
|
#include "../jrd/vio_proto.h"
|
|
#include "../dsql/make_proto.h"
|
|
#include "../dsql/pass1_proto.h"
|
|
#include "../common/StatusArg.h"
|
|
#include "../jrd/Attachment.h"
|
|
|
|
|
|
using namespace Firebird;
|
|
|
|
namespace Jrd {
|
|
|
|
using namespace Firebird;
|
|
using namespace Dsql;
|
|
|
|
DATABASE DB = STATIC "ODS.RDB";
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
namespace
|
|
{
|
|
struct ParameterInfo
|
|
{
|
|
explicit ParameterInfo(MemoryPool& p)
|
|
: type(0),
|
|
number(0),
|
|
name(p),
|
|
fieldSource(p),
|
|
fieldName(p),
|
|
relationName(p),
|
|
mechanism(0)
|
|
{
|
|
defaultSource.clear();
|
|
defaultValue.clear();
|
|
}
|
|
|
|
ParameterInfo(MemoryPool& p, const ParameterInfo& o)
|
|
: type(o.type),
|
|
number(o.number),
|
|
name(p, o.name),
|
|
fieldSource(p, o.fieldSource),
|
|
fieldName(p, o.fieldName),
|
|
relationName(p, o.relationName),
|
|
collationId(o.collationId),
|
|
nullFlag(o.nullFlag),
|
|
mechanism(o.mechanism),
|
|
fieldLength(o.fieldLength),
|
|
fieldScale(o.fieldScale),
|
|
fieldType(o.fieldType),
|
|
fieldSubType(o.fieldSubType),
|
|
fieldSegmentLength(o.fieldSegmentLength),
|
|
fieldNullFlag(o.fieldNullFlag),
|
|
fieldCharLength(o.fieldCharLength),
|
|
fieldCollationId(o.fieldCollationId),
|
|
fieldCharSetId(o.fieldCharSetId),
|
|
fieldPrecision(o.fieldPrecision),
|
|
defaultSource(o.defaultSource),
|
|
defaultValue(o.defaultValue)
|
|
{
|
|
}
|
|
|
|
SSHORT type;
|
|
SSHORT number;
|
|
MetaName name;
|
|
MetaName fieldSource;
|
|
MetaName fieldName;
|
|
MetaName relationName;
|
|
TriStateType<SSHORT> collationId;
|
|
TriStateType<SSHORT> nullFlag;
|
|
SSHORT mechanism;
|
|
TriStateType<SSHORT> fieldLength;
|
|
TriStateType<SSHORT> fieldScale;
|
|
TriStateType<SSHORT> fieldType;
|
|
TriStateType<SSHORT> fieldSubType;
|
|
TriStateType<SSHORT> fieldSegmentLength;
|
|
TriStateType<SSHORT> fieldNullFlag;
|
|
TriStateType<SSHORT> fieldCharLength;
|
|
TriStateType<SSHORT> fieldCollationId;
|
|
TriStateType<SSHORT> fieldCharSetId;
|
|
TriStateType<SSHORT> fieldPrecision;
|
|
|
|
// Not compared
|
|
bid defaultSource;
|
|
bid defaultValue;
|
|
|
|
bool operator >(const ParameterInfo& o) const
|
|
{
|
|
return type > o.type || (type == o.type && number > o.number);
|
|
}
|
|
|
|
bool operator ==(const ParameterInfo& o) const
|
|
{
|
|
return type == o.type && number == o.number && name == o.name &&
|
|
(fieldSource == o.fieldSource ||
|
|
(fb_utils::implicit_domain(fieldSource.c_str()) &&
|
|
fb_utils::implicit_domain(o.fieldSource.c_str()))) &&
|
|
fieldName == o.fieldName && relationName == o.relationName &&
|
|
collationId == o.collationId && nullFlag == o.nullFlag &&
|
|
mechanism == o.mechanism && fieldLength == o.fieldLength &&
|
|
fieldScale == o.fieldScale && fieldType == o.fieldType &&
|
|
fieldSubType == o.fieldSubType && fieldSegmentLength == o.fieldSegmentLength &&
|
|
fieldNullFlag == o.fieldNullFlag && fieldCharLength == o.fieldCharLength &&
|
|
fieldCollationId == o.fieldCollationId && fieldCharSetId == o.fieldCharSetId &&
|
|
fieldPrecision == o.fieldPrecision;
|
|
}
|
|
|
|
bool operator !=(const ParameterInfo& o) const
|
|
{
|
|
return !(*this == o);
|
|
}
|
|
};
|
|
|
|
struct Signature
|
|
{
|
|
Signature(MemoryPool& p, const MetaName& aName)
|
|
: name(p, aName),
|
|
parameters(p),
|
|
defined(false)
|
|
{
|
|
}
|
|
|
|
explicit Signature(const MetaName& aName)
|
|
: name(aName),
|
|
parameters(*getDefaultMemoryPool()),
|
|
defined(false)
|
|
{
|
|
}
|
|
|
|
explicit Signature(MemoryPool& p)
|
|
: name(p),
|
|
parameters(p),
|
|
defined(false)
|
|
{
|
|
}
|
|
|
|
Signature(MemoryPool& p, const Signature& o)
|
|
: name(p, o.name),
|
|
parameters(p),
|
|
defined(o.defined)
|
|
{
|
|
for (SortedObjectsArray<ParameterInfo>::const_iterator i = o.parameters.begin();
|
|
i != o.parameters.end(); ++i)
|
|
{
|
|
parameters.add(*i);
|
|
}
|
|
}
|
|
|
|
bool operator >(const Signature& o) const
|
|
{
|
|
return name > o.name;
|
|
}
|
|
|
|
bool operator ==(const Signature& o) const
|
|
{
|
|
if (name != o.name || parameters.getCount() != o.parameters.getCount())
|
|
return false;
|
|
|
|
for (SortedObjectsArray<ParameterInfo>::const_iterator i = parameters.begin(),
|
|
j = o.parameters.begin();
|
|
i != parameters.end(); ++i, ++j)
|
|
{
|
|
if (*i != *j)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool operator !=(const Signature& o) const
|
|
{
|
|
return !(*this == o);
|
|
}
|
|
|
|
MetaName name;
|
|
SortedObjectsArray<ParameterInfo> parameters;
|
|
bool defined;
|
|
};
|
|
|
|
|
|
// Return function and procedure names (in the user charset) and optionally its details for a
|
|
// given package.
|
|
void collectPackagedItems(thread_db* tdbb, jrd_tra* transaction, const MetaName& metaName,
|
|
SortedObjectsArray<Signature>& functions,
|
|
SortedObjectsArray<Signature>& procedures, bool details)
|
|
{
|
|
AutoCacheRequest requestHandle(tdbb, drq_l_pkg_funcs, DYN_REQUESTS);
|
|
AutoCacheRequest requestHandle2(tdbb, drq_l_pkg_func_args, DYN_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
FUN IN RDB$FUNCTIONS
|
|
WITH FUN.RDB$PACKAGE_NAME EQ metaName.c_str()
|
|
{
|
|
Signature function(FUN.RDB$FUNCTION_NAME);
|
|
function.defined = !FUN.RDB$FUNCTION_BLR.NULL || !FUN.RDB$ENTRYPOINT.NULL;
|
|
|
|
if (details)
|
|
{
|
|
FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction)
|
|
ARG IN RDB$FUNCTION_ARGUMENTS CROSS
|
|
FLD IN RDB$FIELDS
|
|
WITH ARG.RDB$PACKAGE_NAME EQ metaName.c_str() AND
|
|
ARG.RDB$FUNCTION_NAME EQ FUN.RDB$FUNCTION_NAME AND
|
|
FLD.RDB$FIELD_NAME EQ ARG.RDB$FIELD_SOURCE
|
|
{
|
|
ParameterInfo parameter(*getDefaultMemoryPool());
|
|
|
|
parameter.number = ARG.RDB$ARGUMENT_POSITION;
|
|
parameter.name = ARG.RDB$ARGUMENT_NAME;
|
|
parameter.fieldSource = ARG.RDB$FIELD_SOURCE;
|
|
parameter.mechanism = ARG.RDB$ARGUMENT_MECHANISM;
|
|
|
|
if (!ARG.RDB$FIELD_NAME.NULL)
|
|
parameter.fieldName = ARG.RDB$FIELD_NAME;
|
|
if (!ARG.RDB$RELATION_NAME.NULL)
|
|
parameter.relationName = ARG.RDB$RELATION_NAME;
|
|
if (!ARG.RDB$COLLATION_ID.NULL)
|
|
parameter.collationId = ARG.RDB$COLLATION_ID;
|
|
if (!ARG.RDB$NULL_FLAG.NULL)
|
|
parameter.nullFlag = ARG.RDB$NULL_FLAG;
|
|
|
|
if (!FLD.RDB$FIELD_LENGTH.NULL)
|
|
parameter.fieldLength = FLD.RDB$FIELD_LENGTH;
|
|
if (!FLD.RDB$FIELD_SCALE.NULL)
|
|
parameter.fieldScale = FLD.RDB$FIELD_SCALE;
|
|
if (!FLD.RDB$FIELD_TYPE.NULL)
|
|
parameter.fieldType = FLD.RDB$FIELD_TYPE;
|
|
if (!FLD.RDB$FIELD_SUB_TYPE.NULL)
|
|
parameter.fieldSubType = FLD.RDB$FIELD_SUB_TYPE;
|
|
if (!FLD.RDB$SEGMENT_LENGTH.NULL)
|
|
parameter.fieldSegmentLength = FLD.RDB$SEGMENT_LENGTH;
|
|
if (!FLD.RDB$NULL_FLAG.NULL)
|
|
parameter.fieldNullFlag = FLD.RDB$NULL_FLAG;
|
|
if (!FLD.RDB$CHARACTER_LENGTH.NULL)
|
|
parameter.fieldCharLength = FLD.RDB$CHARACTER_LENGTH;
|
|
if (!FLD.RDB$COLLATION_ID.NULL)
|
|
parameter.fieldCollationId = FLD.RDB$COLLATION_ID;
|
|
if (!FLD.RDB$CHARACTER_SET_ID.NULL)
|
|
parameter.fieldCharSetId = FLD.RDB$CHARACTER_SET_ID;
|
|
if (!FLD.RDB$FIELD_PRECISION.NULL)
|
|
parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION;
|
|
|
|
if (!ARG.RDB$DEFAULT_SOURCE.NULL)
|
|
parameter.defaultSource = ARG.RDB$DEFAULT_SOURCE;
|
|
if (!ARG.RDB$DEFAULT_VALUE.NULL)
|
|
parameter.defaultValue = ARG.RDB$DEFAULT_VALUE;
|
|
|
|
function.parameters.add(parameter);
|
|
}
|
|
END_FOR
|
|
}
|
|
|
|
functions.add(function);
|
|
}
|
|
END_FOR
|
|
|
|
requestHandle.reset(tdbb, drq_l_pkg_procs, DYN_REQUESTS);
|
|
requestHandle2.reset(tdbb, drq_l_pkg_proc_args, DYN_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
PRC IN RDB$PROCEDURES
|
|
WITH PRC.RDB$PACKAGE_NAME EQ metaName.c_str()
|
|
{
|
|
Signature procedure(PRC.RDB$PROCEDURE_NAME);
|
|
procedure.defined = !PRC.RDB$PROCEDURE_BLR.NULL || !PRC.RDB$ENTRYPOINT.NULL;
|
|
|
|
if (details)
|
|
{
|
|
FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction)
|
|
PRM IN RDB$PROCEDURE_PARAMETERS CROSS
|
|
FLD IN RDB$FIELDS
|
|
WITH PRM.RDB$PACKAGE_NAME EQ metaName.c_str() AND
|
|
PRM.RDB$PROCEDURE_NAME EQ PRC.RDB$PROCEDURE_NAME AND
|
|
FLD.RDB$FIELD_NAME EQ PRM.RDB$FIELD_SOURCE
|
|
{
|
|
ParameterInfo parameter(*getDefaultMemoryPool());
|
|
parameter.type = PRM.RDB$PARAMETER_TYPE;
|
|
parameter.number = PRM.RDB$PARAMETER_NUMBER;
|
|
parameter.name = PRM.RDB$PARAMETER_NAME;
|
|
parameter.fieldSource = PRM.RDB$FIELD_SOURCE;
|
|
parameter.mechanism = PRM.RDB$PARAMETER_MECHANISM;
|
|
|
|
if (!PRM.RDB$FIELD_NAME.NULL)
|
|
parameter.fieldName = PRM.RDB$FIELD_NAME;
|
|
if (!PRM.RDB$RELATION_NAME.NULL)
|
|
parameter.relationName = PRM.RDB$RELATION_NAME;
|
|
if (!PRM.RDB$COLLATION_ID.NULL)
|
|
parameter.collationId = PRM.RDB$COLLATION_ID;
|
|
if (!PRM.RDB$NULL_FLAG.NULL)
|
|
parameter.nullFlag = PRM.RDB$NULL_FLAG;
|
|
|
|
if (!FLD.RDB$FIELD_LENGTH.NULL)
|
|
parameter.fieldLength = FLD.RDB$FIELD_LENGTH;
|
|
if (!FLD.RDB$FIELD_SCALE.NULL)
|
|
parameter.fieldScale = FLD.RDB$FIELD_SCALE;
|
|
if (!FLD.RDB$FIELD_TYPE.NULL)
|
|
parameter.fieldType = FLD.RDB$FIELD_TYPE;
|
|
if (!FLD.RDB$FIELD_SUB_TYPE.NULL)
|
|
parameter.fieldSubType = FLD.RDB$FIELD_SUB_TYPE;
|
|
if (!FLD.RDB$SEGMENT_LENGTH.NULL)
|
|
parameter.fieldSegmentLength = FLD.RDB$SEGMENT_LENGTH;
|
|
if (!FLD.RDB$NULL_FLAG.NULL)
|
|
parameter.fieldNullFlag = FLD.RDB$NULL_FLAG;
|
|
if (!FLD.RDB$CHARACTER_LENGTH.NULL)
|
|
parameter.fieldCharLength = FLD.RDB$CHARACTER_LENGTH;
|
|
if (!FLD.RDB$COLLATION_ID.NULL)
|
|
parameter.fieldCollationId = FLD.RDB$COLLATION_ID;
|
|
if (!FLD.RDB$CHARACTER_SET_ID.NULL)
|
|
parameter.fieldCharSetId = FLD.RDB$CHARACTER_SET_ID;
|
|
if (!FLD.RDB$FIELD_PRECISION.NULL)
|
|
parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION;
|
|
|
|
if (!PRM.RDB$DEFAULT_SOURCE.NULL)
|
|
parameter.defaultSource = PRM.RDB$DEFAULT_SOURCE;
|
|
if (!PRM.RDB$DEFAULT_VALUE.NULL)
|
|
parameter.defaultValue = PRM.RDB$DEFAULT_VALUE;
|
|
|
|
procedure.parameters.add(parameter);
|
|
}
|
|
END_FOR
|
|
}
|
|
|
|
procedures.add(procedure);
|
|
}
|
|
END_FOR
|
|
}
|
|
} // namespace
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
void CreateAlterPackageNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
|
{
|
|
fb_assert(items);
|
|
|
|
text.printf(
|
|
"CreateAlterPackageNode\n"
|
|
" name: '%s' create: %d alter: %d\n"
|
|
" Items:\n"
|
|
"--------\n",
|
|
name.c_str(), create, alter);
|
|
|
|
for (unsigned i = 0; i < items->getCount(); ++i)
|
|
{
|
|
string item;
|
|
Array<dsql_nod*> tempNodes;
|
|
|
|
switch ((*items)[i].type)
|
|
{
|
|
case Item::FUNCTION:
|
|
(*items)[i].function->print(item, tempNodes);
|
|
break;
|
|
|
|
case Item::PROCEDURE:
|
|
(*items)[i].procedure->print(item, tempNodes);
|
|
break;
|
|
}
|
|
|
|
text += item;
|
|
}
|
|
|
|
text += "--------\n";
|
|
}
|
|
|
|
|
|
DdlNode* CreateAlterPackageNode::internalDsqlPass()
|
|
{
|
|
source.ltrim("\n\r\t ");
|
|
|
|
// items
|
|
for (unsigned i = 0; i < items->getCount(); ++i)
|
|
{
|
|
DsqlCompiledStatement* itemStatement = FB_NEW(getPool()) DsqlCompiledStatement(getPool());
|
|
DsqlCompilerScratch* itemScratch = FB_NEW(getPool()) DsqlCompilerScratch(getPool(),
|
|
dsqlScratch->getAttachment(), dsqlScratch->getTransaction(), itemStatement);
|
|
itemScratch->clientDialect = dsqlScratch->clientDialect;
|
|
itemScratch->package = name;
|
|
itemStatement->setDdlNode(MAKE_node(nod_class_stmtnode, 1));
|
|
|
|
switch ((*items)[i].type)
|
|
{
|
|
case CreateAlterPackageNode::Item::FUNCTION:
|
|
{
|
|
CreateAlterFunctionNode* const fun = (*items)[i].function;
|
|
functionNames.add(fun->name);
|
|
fun->alter = true;
|
|
fun->package = name;
|
|
itemStatement->getDdlNode()->nod_arg[0] = (dsql_nod*) fun;
|
|
fun->dsqlPass(itemScratch);
|
|
}
|
|
break;
|
|
|
|
case CreateAlterPackageNode::Item::PROCEDURE:
|
|
{
|
|
CreateAlterProcedureNode* const proc = (*items)[i].procedure;
|
|
procedureNames.add(proc->name);
|
|
proc->alter = true;
|
|
proc->package = name;
|
|
itemStatement->getDdlNode()->nod_arg[0] = (dsql_nod*) proc;
|
|
proc->dsqlPass(itemScratch);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return DdlNode::internalDsqlPass();
|
|
}
|
|
|
|
|
|
void CreateAlterPackageNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
{
|
|
fb_assert(create || alter);
|
|
|
|
//Database* dbb = tdbb->getDatabase();
|
|
|
|
//dbb->checkOdsForDsql(ODS_12_0);
|
|
|
|
// run all statements under savepoint control
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
if (alter)
|
|
{
|
|
if (!executeAlter(tdbb, transaction))
|
|
{
|
|
if (create) // create or alter
|
|
executeCreate(tdbb, transaction);
|
|
else
|
|
{
|
|
status_exception::raise(
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
Arg::Gds(isc_dyn_package_not_found) << Arg::Str(name));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
executeCreate(tdbb, transaction);
|
|
|
|
// items
|
|
for (unsigned i = 0; i < items->getCount(); ++i)
|
|
{
|
|
switch ((*items)[i].type)
|
|
{
|
|
case Item::FUNCTION:
|
|
(*items)[i].function->packageOwner = owner;
|
|
(*items)[i].function->executeDdl(tdbb, transaction);
|
|
break;
|
|
|
|
case Item::PROCEDURE:
|
|
(*items)[i].procedure->packageOwner = owner;
|
|
(*items)[i].procedure->executeDdl(tdbb, transaction);
|
|
break;
|
|
}
|
|
}
|
|
|
|
savePoint.release(); // everything is ok
|
|
}
|
|
|
|
|
|
void CreateAlterPackageNode::executeCreate(thread_db* tdbb, jrd_tra* transaction)
|
|
{
|
|
Attachment* attachment = transaction->getAttachment();
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_PACKAGE, name);
|
|
|
|
AutoCacheRequest requestHandle(tdbb, drq_s_pkg, DYN_REQUESTS);
|
|
|
|
STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
PKG IN RDB$PACKAGES USING
|
|
{
|
|
PKG.RDB$PACKAGE_NAME.NULL = FALSE;
|
|
strcpy(PKG.RDB$PACKAGE_NAME, name.c_str());
|
|
|
|
PKG.RDB$SYSTEM_FLAG.NULL = FALSE;
|
|
PKG.RDB$SYSTEM_FLAG = 0;
|
|
|
|
PKG.RDB$OWNER_NAME.NULL = FALSE;
|
|
strcpy(PKG.RDB$OWNER_NAME, attachment->att_user->usr_user_name.c_str());
|
|
|
|
PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE;
|
|
attachment->storeMetaDataBlob(tdbb, transaction, &PKG.RDB$PACKAGE_HEADER_SOURCE, source);
|
|
}
|
|
END_STORE
|
|
|
|
for (const TEXT* p = ALL_PROC_PRIVILEGES; *p; p++)
|
|
{
|
|
requestHandle.reset(tdbb, drq_s_pkg_usr_prvs, DYN_REQUESTS);
|
|
|
|
STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
X IN RDB$USER_PRIVILEGES
|
|
{
|
|
strcpy(X.RDB$RELATION_NAME, name.c_str());
|
|
strcpy(X.RDB$USER, attachment->att_user->usr_user_name.c_str());
|
|
X.RDB$USER_TYPE = obj_user;
|
|
X.RDB$OBJECT_TYPE = obj_package_header;
|
|
X.RDB$PRIVILEGE[0] = *p;
|
|
X.RDB$PRIVILEGE[1] = 0;
|
|
}
|
|
END_STORE;
|
|
}
|
|
|
|
owner = attachment->att_user->usr_user_name;
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_PACKAGE, name);
|
|
}
|
|
|
|
|
|
bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, jrd_tra* transaction)
|
|
{
|
|
Attachment* attachment = transaction->getAttachment();
|
|
AutoCacheRequest requestHandle(tdbb, drq_m_pkg, DYN_REQUESTS);
|
|
bool modified = false;
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
PKG IN RDB$PACKAGES
|
|
WITH PKG.RDB$PACKAGE_NAME EQ name.c_str()
|
|
{
|
|
modified = true;
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_PACKAGE, name);
|
|
|
|
SortedObjectsArray<Signature> existingFuncs(getPool());
|
|
SortedObjectsArray<Signature> existingProcs(getPool());
|
|
collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, false);
|
|
|
|
for (SortedObjectsArray<Signature>::iterator i = existingFuncs.begin();
|
|
i != existingFuncs.end(); ++i)
|
|
{
|
|
if (!functionNames.exist(i->name))
|
|
{
|
|
DropFunctionNode dropNode(getPool(), "", i->name);
|
|
dropNode.package = name;
|
|
dropNode.dsqlPass(dsqlScratch);
|
|
dropNode.executeDdl(tdbb, transaction);
|
|
}
|
|
}
|
|
|
|
for (SortedObjectsArray<Signature>::iterator i = existingProcs.begin();
|
|
i != existingProcs.end(); ++i)
|
|
{
|
|
if (!procedureNames.exist(i->name))
|
|
{
|
|
DropProcedureNode dropNode(getPool(), "", i->name);
|
|
dropNode.package = name;
|
|
dropNode.dsqlPass(dsqlScratch);
|
|
dropNode.executeDdl(tdbb, transaction);
|
|
}
|
|
}
|
|
|
|
MODIFY PKG
|
|
PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE;
|
|
attachment->storeMetaDataBlob(tdbb, transaction, &PKG.RDB$PACKAGE_HEADER_SOURCE,
|
|
getSqlText());
|
|
|
|
PKG.RDB$PACKAGE_BODY_SOURCE.NULL = TRUE;
|
|
END_MODIFY
|
|
|
|
owner = PKG.RDB$OWNER_NAME;
|
|
}
|
|
END_FOR
|
|
|
|
if (modified)
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_PACKAGE, name);
|
|
|
|
return modified;
|
|
}
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
void DropPackageNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
|
{
|
|
text.printf(
|
|
"DropPackageNode\n"
|
|
" name: '%s'\n",
|
|
name.c_str());
|
|
}
|
|
|
|
|
|
void DropPackageNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
{
|
|
// run all statements under savepoint control
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
bool found = false;
|
|
AutoCacheRequest requestHandle(tdbb, drq_e_pkg, DYN_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
PKG IN RDB$PACKAGES
|
|
WITH PKG.RDB$PACKAGE_NAME EQ name.c_str()
|
|
{
|
|
found = true;
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_PACKAGE, name);
|
|
|
|
ERASE PKG;
|
|
|
|
if (!PKG.RDB$SECURITY_CLASS.NULL)
|
|
DYN_delete_security_class2(transaction, PKG.RDB$SECURITY_CLASS);
|
|
|
|
dsc desc;
|
|
desc.makeText(name.length(), ttype_metadata,
|
|
(UCHAR*) const_cast<char*>(name.c_str())); // safe const_cast
|
|
DFW_post_work(transaction, dfw_drop_package_header, &desc, 0);
|
|
}
|
|
END_FOR
|
|
|
|
if (!found && !silent)
|
|
{
|
|
status_exception::raise(
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
Arg::Gds(isc_dyn_package_not_found) << Arg::Str(name));
|
|
}
|
|
|
|
SortedObjectsArray<Signature> existingFuncs(getPool());
|
|
SortedObjectsArray<Signature> existingProcs(getPool());
|
|
collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, false);
|
|
|
|
for (SortedObjectsArray<Signature>::iterator i = existingFuncs.begin();
|
|
i != existingFuncs.end(); ++i)
|
|
{
|
|
DropFunctionNode dropNode(getPool(), "", i->name);
|
|
dropNode.package = name;
|
|
dropNode.dsqlPass(dsqlScratch);
|
|
dropNode.executeDdl(tdbb, transaction);
|
|
}
|
|
|
|
for (SortedObjectsArray<Signature>::iterator i = existingProcs.begin();
|
|
i != existingProcs.end(); ++i)
|
|
{
|
|
DropProcedureNode dropNode(getPool(), "", i->name);
|
|
dropNode.package = name;
|
|
dropNode.dsqlPass(dsqlScratch);
|
|
dropNode.executeDdl(tdbb, transaction);
|
|
}
|
|
|
|
requestHandle.reset(tdbb, drq_e_pkg_prv, DYN_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
PRIV IN RDB$USER_PRIVILEGES
|
|
WITH (PRIV.RDB$RELATION_NAME EQ name.c_str() AND
|
|
PRIV.RDB$OBJECT_TYPE = obj_package_header) OR
|
|
(PRIV.RDB$USER EQ name.c_str() AND PRIV.RDB$USER_TYPE = obj_package_header)
|
|
{
|
|
ERASE PRIV;
|
|
}
|
|
END_FOR
|
|
|
|
if (found)
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PACKAGE, name);
|
|
|
|
savePoint.release(); // everything is ok
|
|
}
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
void RecreatePackageNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
|
{
|
|
text.printf("RecreatePackageNode\n");
|
|
}
|
|
|
|
|
|
DdlNode* RecreatePackageNode::internalDsqlPass()
|
|
{
|
|
dropNode.dsqlPass(dsqlScratch);
|
|
createNode->dsqlPass(dsqlScratch);
|
|
return DdlNode::internalDsqlPass();
|
|
}
|
|
|
|
|
|
void RecreatePackageNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
{
|
|
// run all statements under savepoint control
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
dropNode.executeDdl(tdbb, transaction);
|
|
createNode->executeDdl(tdbb, transaction);
|
|
|
|
savePoint.release(); // everything is ok
|
|
}
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
void CreatePackageBodyNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
|
{
|
|
fb_assert(items);
|
|
|
|
text.printf(
|
|
"CreatePackageBodyNode\n"
|
|
" name: '%s'\n"
|
|
" declaredItems:\n"
|
|
"--------\n",
|
|
name.c_str());
|
|
|
|
if (declaredItems)
|
|
{
|
|
for (unsigned i = 0; i < declaredItems->getCount(); ++i)
|
|
{
|
|
string item;
|
|
Array<dsql_nod*> tempNodes;
|
|
|
|
switch ((*declaredItems)[i].type)
|
|
{
|
|
case CreateAlterPackageNode::Item::FUNCTION:
|
|
(*declaredItems)[i].function->print(item, tempNodes);
|
|
break;
|
|
|
|
case CreateAlterPackageNode::Item::PROCEDURE:
|
|
(*declaredItems)[i].procedure->print(item, tempNodes);
|
|
break;
|
|
}
|
|
|
|
text += item;
|
|
}
|
|
}
|
|
|
|
text +=
|
|
"--------\n"
|
|
" items:\n"
|
|
"--------\n";
|
|
|
|
for (unsigned i = 0; i < items->getCount(); ++i)
|
|
{
|
|
string item;
|
|
Array<dsql_nod*> tempNodes;
|
|
|
|
switch ((*items)[i].type)
|
|
{
|
|
case CreateAlterPackageNode::Item::FUNCTION:
|
|
(*items)[i].function->print(item, tempNodes);
|
|
break;
|
|
|
|
case CreateAlterPackageNode::Item::PROCEDURE:
|
|
(*items)[i].procedure->print(item, tempNodes);
|
|
break;
|
|
}
|
|
|
|
text += item;
|
|
}
|
|
|
|
text += "--------\n";
|
|
}
|
|
|
|
|
|
DdlNode* CreatePackageBodyNode::internalDsqlPass()
|
|
{
|
|
source.ltrim("\n\r\t ");
|
|
|
|
// process declaredItems and items
|
|
Array<CreateAlterPackageNode::Item>* arrays[] = {declaredItems, items};
|
|
|
|
for (unsigned i = 0; i < FB_NELEM(arrays); ++i)
|
|
{
|
|
if (!arrays[i])
|
|
continue;
|
|
|
|
for (unsigned j = 0; j < arrays[i]->getCount(); ++j)
|
|
{
|
|
DsqlCompiledStatement* itemStatement = FB_NEW(getPool()) DsqlCompiledStatement(getPool());
|
|
DsqlCompilerScratch* itemScratch = FB_NEW(getPool()) DsqlCompilerScratch(getPool(),
|
|
dsqlScratch->getAttachment(), dsqlScratch->getTransaction(), itemStatement);
|
|
itemScratch->clientDialect = dsqlScratch->clientDialect;
|
|
itemScratch->package = name;
|
|
itemStatement->setDdlNode(MAKE_node(nod_class_stmtnode, 1));
|
|
|
|
switch ((*arrays[i])[j].type)
|
|
{
|
|
case CreateAlterPackageNode::Item::FUNCTION:
|
|
{
|
|
CreateAlterFunctionNode* const fun = (*arrays[i])[j].function;
|
|
fun->package = name;
|
|
fun->create = true;
|
|
if (arrays[i] == items)
|
|
fun->alter = true;
|
|
itemStatement->getDdlNode()->nod_arg[0] = (dsql_nod*) fun;
|
|
fun->dsqlPass(itemScratch);
|
|
}
|
|
break;
|
|
|
|
case CreateAlterPackageNode::Item::PROCEDURE:
|
|
{
|
|
CreateAlterProcedureNode* const proc = (*arrays[i])[j].procedure;
|
|
proc->package = name;
|
|
proc->create = true;
|
|
if (arrays[i] == items)
|
|
proc->alter = true;
|
|
itemStatement->getDdlNode()->nod_arg[0] = (dsql_nod*) proc;
|
|
proc->dsqlPass(itemScratch);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return DdlNode::internalDsqlPass();
|
|
}
|
|
|
|
|
|
void CreatePackageBodyNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
{
|
|
Attachment* attachment = transaction->getAttachment();
|
|
|
|
// run all statements under savepoint control
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
AutoCacheRequest requestHandle(tdbb, drq_m_pkg_body, DYN_REQUESTS);
|
|
bool modified = false;
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
PKG IN RDB$PACKAGES
|
|
WITH PKG.RDB$PACKAGE_NAME EQ name.c_str()
|
|
{
|
|
if (!PKG.RDB$PACKAGE_BODY_SOURCE.NULL)
|
|
{
|
|
status_exception::raise(
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
Arg::Gds(isc_dyn_package_body_exists) << Arg::Str(name));
|
|
}
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_PACKAGE_BODY, name);
|
|
|
|
MODIFY PKG
|
|
PKG.RDB$PACKAGE_BODY_SOURCE.NULL = FALSE;
|
|
attachment->storeMetaDataBlob(tdbb, transaction, &PKG.RDB$PACKAGE_BODY_SOURCE, source);
|
|
END_MODIFY
|
|
|
|
modified = true;
|
|
|
|
owner = PKG.RDB$OWNER_NAME;
|
|
}
|
|
END_FOR
|
|
|
|
if (!modified)
|
|
{
|
|
status_exception::raise(
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
Arg::Gds(isc_dyn_package_not_found) << Arg::Str(name));
|
|
}
|
|
|
|
SortedObjectsArray<Signature> headerFuncs(getPool());
|
|
SortedObjectsArray<Signature> headerProcs(getPool());
|
|
collectPackagedItems(tdbb, transaction, name, headerFuncs, headerProcs, false);
|
|
|
|
SortedObjectsArray<Signature> existingFuncs(getPool());
|
|
SortedObjectsArray<Signature> existingProcs(getPool());
|
|
|
|
// process declaredItems and items
|
|
Array<CreateAlterPackageNode::Item>* arrays[] = {declaredItems, items};
|
|
|
|
for (unsigned i = 0; i < FB_NELEM(arrays); ++i)
|
|
{
|
|
if (!arrays[i])
|
|
continue;
|
|
|
|
if (arrays[i] == items)
|
|
collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, true);
|
|
|
|
for (unsigned j = 0; j < arrays[i]->getCount(); ++j)
|
|
{
|
|
switch ((*arrays[i])[j].type)
|
|
{
|
|
case CreateAlterPackageNode::Item::FUNCTION:
|
|
{
|
|
CreateAlterFunctionNode* func = (*arrays[i])[j].function;
|
|
|
|
if (arrays[i] == items)
|
|
func->privateScope = !headerFuncs.exist(Signature(func->name));
|
|
|
|
func->packageOwner = owner;
|
|
func->executeDdl(tdbb, transaction);
|
|
}
|
|
break;
|
|
|
|
case CreateAlterPackageNode::Item::PROCEDURE:
|
|
{
|
|
CreateAlterProcedureNode* proc = (*arrays[i])[j].procedure;
|
|
|
|
if (arrays[i] == items)
|
|
proc->privateScope = !headerProcs.exist(Signature(proc->name));
|
|
|
|
proc->packageOwner = owner;
|
|
proc->executeDdl(tdbb, transaction);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
SortedObjectsArray<Signature> newFuncs(getPool());
|
|
SortedObjectsArray<Signature> newProcs(getPool());
|
|
collectPackagedItems(tdbb, transaction, name, newFuncs, newProcs, true);
|
|
|
|
for (SortedObjectsArray<Signature>::iterator i = existingFuncs.begin();
|
|
i != existingFuncs.end(); ++i)
|
|
{
|
|
size_t pos;
|
|
bool found = newFuncs.find(Signature(getPool(), i->name), pos);
|
|
|
|
if (!found || !newFuncs[pos].defined)
|
|
{
|
|
status_exception::raise(
|
|
Arg::Gds(isc_dyn_funcnotdef_package) << i->name.c_str() << name.c_str());
|
|
}
|
|
else if (newFuncs[pos] != *i)
|
|
{
|
|
status_exception::raise(
|
|
Arg::Gds(isc_dyn_funcsignat_package) << i->name.c_str() << name.c_str());
|
|
}
|
|
}
|
|
|
|
for (SortedObjectsArray<Signature>::iterator i = existingProcs.begin();
|
|
i != existingProcs.end(); ++i)
|
|
{
|
|
size_t pos;
|
|
bool found = newProcs.find(Signature(getPool(), i->name), pos);
|
|
|
|
if (!found || !newProcs[pos].defined)
|
|
{
|
|
status_exception::raise(
|
|
Arg::Gds(isc_dyn_procnotdef_package) << i->name.c_str() << name.c_str());
|
|
}
|
|
else if (newProcs[pos] != *i)
|
|
{
|
|
status_exception::raise(
|
|
Arg::Gds(isc_dyn_procsignat_package) << i->name.c_str() << name.c_str());
|
|
}
|
|
|
|
for (SortedObjectsArray<ParameterInfo>::iterator j = newProcs[pos].parameters.begin();
|
|
j != newProcs[pos].parameters.end(); ++j)
|
|
{
|
|
if (!j->defaultSource.isEmpty() || !j->defaultValue.isEmpty())
|
|
{
|
|
status_exception::raise(
|
|
Arg::Gds(isc_dyn_defvaldecl_package) << name.c_str() << i->name.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Lets recreate default of public procedure parameters
|
|
|
|
requestHandle.reset(tdbb, drq_m_pkg_prm_defs, DYN_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
PRM IN RDB$PROCEDURE_PARAMETERS
|
|
WITH PRM.RDB$PACKAGE_NAME EQ name.c_str()
|
|
{
|
|
size_t pos;
|
|
if (existingProcs.find(Signature(getPool(), MetaName(PRM.RDB$PROCEDURE_NAME)), pos))
|
|
{
|
|
const Signature& proc = existingProcs[pos];
|
|
|
|
ParameterInfo paramKey(getPool());
|
|
paramKey.type = PRM.RDB$PARAMETER_TYPE;
|
|
paramKey.number = PRM.RDB$PARAMETER_NUMBER;
|
|
|
|
if (proc.parameters.find(paramKey, pos))
|
|
{
|
|
const ParameterInfo& param = proc.parameters[pos];
|
|
|
|
MODIFY PRM
|
|
PRM.RDB$DEFAULT_SOURCE = param.defaultSource;
|
|
PRM.RDB$DEFAULT_SOURCE.NULL = param.defaultSource.isEmpty();
|
|
|
|
PRM.RDB$DEFAULT_VALUE = param.defaultValue;
|
|
PRM.RDB$DEFAULT_VALUE.NULL = param.defaultValue.isEmpty();
|
|
END_MODIFY
|
|
}
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_PACKAGE_BODY, name);
|
|
|
|
savePoint.release(); // everything is ok
|
|
}
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
void DropPackageBodyNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
|
{
|
|
text.printf(
|
|
"DropPackageBodyNode\n"
|
|
" name: '%s'\n",
|
|
name.c_str());
|
|
}
|
|
|
|
|
|
void DropPackageBodyNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
{
|
|
// run all statements under savepoint control
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
bool found = false;
|
|
AutoCacheRequest requestHandle(tdbb, drq_m_pkg_body2, DYN_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
PKG IN RDB$PACKAGES
|
|
WITH PKG.RDB$PACKAGE_NAME EQ name.c_str()
|
|
{
|
|
found = true;
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_PACKAGE_BODY, name);
|
|
|
|
MODIFY PKG
|
|
PKG.RDB$PACKAGE_BODY_SOURCE.NULL = TRUE;
|
|
|
|
dsc desc;
|
|
desc.makeText(name.length(), ttype_metadata,
|
|
(UCHAR*) const_cast<char*>(name.c_str())); // safe const_cast
|
|
DFW_post_work(transaction, dfw_drop_package_body, &desc, 0);
|
|
END_MODIFY
|
|
}
|
|
END_FOR
|
|
|
|
if (!found)
|
|
{
|
|
status_exception::raise(
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
Arg::Gds(isc_dyn_package_not_found) << Arg::Str(name));
|
|
}
|
|
|
|
requestHandle.reset(tdbb, drq_m_pkg_fun, DYN_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
FUN IN RDB$FUNCTIONS
|
|
WITH FUN.RDB$PACKAGE_NAME EQ name.c_str()
|
|
{
|
|
if (!FUN.RDB$PRIVATE_FLAG.NULL && FUN.RDB$PRIVATE_FLAG != 0)
|
|
{
|
|
DropFunctionNode dropNode(getPool(), "", FUN.RDB$FUNCTION_NAME);
|
|
dropNode.package = name;
|
|
dropNode.dsqlPass(dsqlScratch);
|
|
dropNode.executeDdl(tdbb, transaction);
|
|
}
|
|
else
|
|
{
|
|
MODIFY FUN
|
|
FUN.RDB$FUNCTION_TYPE.NULL = TRUE;
|
|
FUN.RDB$FUNCTION_BLR.NULL = TRUE;
|
|
FUN.RDB$DEBUG_INFO.NULL = TRUE;
|
|
FUN.RDB$MODULE_NAME.NULL = TRUE;
|
|
FUN.RDB$ENGINE_NAME.NULL = TRUE;
|
|
FUN.RDB$ENTRYPOINT.NULL = TRUE;
|
|
END_MODIFY
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
requestHandle.reset(tdbb, drq_m_pkg_prc, DYN_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
PRC IN RDB$PROCEDURES
|
|
WITH PRC.RDB$PACKAGE_NAME EQ name.c_str()
|
|
{
|
|
if (!PRC.RDB$PRIVATE_FLAG.NULL && PRC.RDB$PRIVATE_FLAG != 0)
|
|
{
|
|
DropProcedureNode dropNode(getPool(), "", PRC.RDB$PROCEDURE_NAME);
|
|
dropNode.package = name;
|
|
dropNode.dsqlPass(dsqlScratch);
|
|
dropNode.executeDdl(tdbb, transaction);
|
|
}
|
|
else
|
|
{
|
|
MODIFY PRC
|
|
PRC.RDB$PROCEDURE_TYPE.NULL = TRUE;
|
|
PRC.RDB$PROCEDURE_BLR.NULL = TRUE;
|
|
PRC.RDB$DEBUG_INFO.NULL = TRUE;
|
|
PRC.RDB$ENGINE_NAME.NULL = TRUE;
|
|
PRC.RDB$ENTRYPOINT.NULL = TRUE;
|
|
END_MODIFY
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PACKAGE_BODY, name);
|
|
|
|
savePoint.release(); // everything is ok
|
|
}
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
void RecreatePackageBodyNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
|
{
|
|
text.printf("RecreatePackageBodyNode\n");
|
|
}
|
|
|
|
|
|
DdlNode* RecreatePackageBodyNode::internalDsqlPass()
|
|
{
|
|
dropNode.dsqlPass(dsqlScratch);
|
|
createNode->dsqlPass(dsqlScratch);
|
|
return DdlNode::internalDsqlPass();
|
|
}
|
|
|
|
|
|
void RecreatePackageBodyNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
{
|
|
// run all statements under savepoint control
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
dropNode.executeDdl(tdbb, transaction);
|
|
createNode->executeDdl(tdbb, transaction);
|
|
|
|
savePoint.release(); // everything is ok
|
|
}
|
|
|
|
|
|
} // namespace Jrd
|