2008-05-19 15:47:48 +02: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, ddl.cpp, dyn*.epp
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "firebird.h"
|
|
|
|
#include "../jrd/common.h"
|
|
|
|
#include "../dsql/DdlNodes.h"
|
2009-10-21 02:42:38 +02:00
|
|
|
#include "../dsql/node.h"
|
|
|
|
#include "../jrd/blr.h"
|
|
|
|
#include "../jrd/dyn.h"
|
|
|
|
#include "../jrd/flags.h"
|
|
|
|
#include "../jrd/intl.h"
|
2008-05-19 15:47:48 +02:00
|
|
|
#include "../jrd/jrd.h"
|
2010-01-10 17:39:56 +01:00
|
|
|
#include "../jrd/msg_encode.h"
|
2009-10-21 02:42:38 +02:00
|
|
|
#include "../jrd/obj.h"
|
|
|
|
#include "../jrd/tra.h"
|
2010-06-26 03:52:06 +02:00
|
|
|
#include "../jrd/IntlManager.h"
|
2009-10-21 02:42:38 +02:00
|
|
|
#include "../jrd/PreparedStatement.h"
|
|
|
|
#include "../jrd/blb_proto.h"
|
2008-05-19 15:47:48 +02:00
|
|
|
#include "../jrd/cmp_proto.h"
|
2010-07-06 02:49:33 +02:00
|
|
|
#include "../jrd/dsc_proto.h"
|
2009-10-21 02:42:38 +02:00
|
|
|
#include "../jrd/dyn_dl_proto.h"
|
|
|
|
#include "../jrd/dyn_ut_proto.h"
|
2008-05-19 15:47:48 +02:00
|
|
|
#include "../jrd/exe_proto.h"
|
2009-10-21 02:42:38 +02:00
|
|
|
#include "../jrd/intl_proto.h"
|
2008-05-19 15:47:48 +02:00
|
|
|
#include "../jrd/met_proto.h"
|
2009-10-21 02:42:38 +02:00
|
|
|
#include "../jrd/vio_proto.h"
|
|
|
|
#include "../dsql/ddl_proto.h"
|
|
|
|
#include "../dsql/errd_proto.h"
|
|
|
|
#include "../dsql/gen_proto.h"
|
|
|
|
#include "../dsql/make_proto.h"
|
2008-05-19 15:47:48 +02:00
|
|
|
#include "../dsql/metd_proto.h"
|
2009-10-21 02:42:38 +02:00
|
|
|
#include "../dsql/pass1_proto.h"
|
|
|
|
#include "../common/StatusArg.h"
|
2008-05-19 15:47:48 +02:00
|
|
|
|
2008-07-03 14:02:54 +02:00
|
|
|
using namespace Firebird;
|
|
|
|
|
2008-05-19 15:47:48 +02:00
|
|
|
namespace Jrd {
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
using namespace Firebird;
|
|
|
|
using namespace Dsql;
|
2008-05-19 15:47:48 +02:00
|
|
|
|
2010-01-10 17:39:56 +01:00
|
|
|
static void rethrowMetaException(const status_exception& ex, ISC_STATUS code, bool metaDataException);
|
|
|
|
|
2008-05-19 15:47:48 +02:00
|
|
|
DATABASE DB = STATIC "ODS.RDB";
|
|
|
|
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
2010-01-10 17:39:56 +01:00
|
|
|
// Rethrow an exception with isc_no_meta_update and code prefix when necessary.
|
|
|
|
static void rethrowMetaException(const status_exception& ex, ISC_STATUS code, bool metaDataException)
|
|
|
|
{
|
|
|
|
Arg::StatusVector newVector;
|
|
|
|
const ISC_STATUS* status = ex.value();
|
|
|
|
|
|
|
|
if (metaDataException)
|
|
|
|
{
|
|
|
|
if (status[1] != isc_no_meta_update)
|
|
|
|
newVector << Arg::Gds(isc_no_meta_update);
|
|
|
|
|
|
|
|
if (code != 0)
|
|
|
|
newVector << Arg::Gds(code);
|
|
|
|
}
|
|
|
|
|
|
|
|
newVector.append(Arg::StatusVector(status));
|
|
|
|
status_exception::raise(newVector);
|
|
|
|
}
|
|
|
|
|
2010-07-06 02:49:33 +02:00
|
|
|
// Update RDB$FIELDS received by reference.
|
|
|
|
static void updateRdbFields(const TypeClause& type,
|
|
|
|
SSHORT& fieldType,
|
|
|
|
SSHORT& fieldLength,
|
|
|
|
SSHORT& fieldSubTypeNull, SSHORT& fieldSubType,
|
|
|
|
SSHORT& fieldScaleNull, SSHORT& fieldScale,
|
|
|
|
SSHORT& characterSetIdNull, SSHORT& characterSetId,
|
|
|
|
SSHORT& characterLengthNull, SSHORT& characterLength,
|
|
|
|
SSHORT& fieldPrecisionNull, SSHORT& fieldPrecision,
|
|
|
|
SSHORT& collationIdNull, SSHORT& collationId,
|
|
|
|
SSHORT& segmentLengthNull, SSHORT& segmentLength)
|
|
|
|
{
|
|
|
|
// Initialize all nullable fields.
|
|
|
|
fieldSubTypeNull = fieldScaleNull = characterSetIdNull = characterLengthNull =
|
|
|
|
fieldPrecisionNull = collationIdNull = segmentLengthNull = TRUE;
|
|
|
|
|
|
|
|
if (type.type == dtype_blob)
|
|
|
|
{
|
|
|
|
fieldSubTypeNull = FALSE;
|
|
|
|
fieldSubType = type.subType;
|
|
|
|
|
|
|
|
fieldScaleNull = FALSE;
|
|
|
|
fieldScale = 0;
|
|
|
|
|
|
|
|
if (type.subType == isc_blob_text)
|
|
|
|
{
|
|
|
|
characterSetIdNull = FALSE;
|
|
|
|
characterSetId = type.charSetId;
|
|
|
|
|
|
|
|
collationIdNull = !type.collateSpecified;
|
|
|
|
collationId = type.collationId;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type.segLength != 0)
|
|
|
|
{
|
|
|
|
segmentLengthNull = FALSE;
|
|
|
|
segmentLength = type.segLength;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (type.type <= dtype_any_text)
|
|
|
|
{
|
|
|
|
fieldSubTypeNull = FALSE;
|
|
|
|
fieldSubType = type.subType;
|
|
|
|
|
|
|
|
fieldScaleNull = FALSE;
|
|
|
|
fieldScale = 0;
|
|
|
|
|
|
|
|
characterLengthNull = FALSE;
|
|
|
|
characterLength = type.charLength;
|
|
|
|
|
|
|
|
characterSetIdNull = FALSE;
|
|
|
|
characterSetId = type.charSetId;
|
|
|
|
|
|
|
|
collationIdNull = !type.collateSpecified;
|
|
|
|
collationId = type.collationId;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fieldScaleNull = FALSE;
|
|
|
|
fieldScale = type.scale;
|
|
|
|
|
|
|
|
if (DTYPE_IS_EXACT(type.type))
|
|
|
|
{
|
|
|
|
fieldPrecisionNull = FALSE;
|
|
|
|
fieldPrecision = type.precision;
|
|
|
|
|
|
|
|
fieldSubTypeNull = FALSE;
|
|
|
|
fieldSubType = type.subType;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type.type == dtype_varying)
|
|
|
|
{
|
|
|
|
fb_assert(type.length <= MAX_SSHORT);
|
|
|
|
fieldLength = (SSHORT) (type.length - sizeof(USHORT));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fieldLength = type.length;
|
|
|
|
|
|
|
|
fieldType = blr_dtypes[type.type];
|
|
|
|
}
|
|
|
|
|
2010-01-10 17:39:56 +01:00
|
|
|
|
2010-01-10 18:32:40 +01:00
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
2010-01-10 18:35:11 +01:00
|
|
|
void DdlNode::executeDdlTrigger(thread_db* tdbb, jrd_tra* transaction, DdlTriggerWhen when,
|
|
|
|
int action, const MetaName& objectName, const string& sqlText)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
Attachment* attachment = transaction->tra_attachment;
|
|
|
|
|
|
|
|
// do nothing if user doesn't want database triggers
|
|
|
|
if (attachment->att_flags & ATT_no_db_triggers)
|
|
|
|
return;
|
|
|
|
|
2009-10-31 02:46:06 +01:00
|
|
|
fb_assert(action > 0); // first element is NULL
|
2009-10-21 02:42:38 +02:00
|
|
|
DdlTriggerContext context;
|
2009-10-27 15:50:15 +01:00
|
|
|
context.ddlEvent = DDL_TRIGGER_ACTION_NAMES[action];
|
2009-10-21 02:42:38 +02:00
|
|
|
context.objectName = objectName;
|
|
|
|
context.sqlText = sqlText;
|
|
|
|
|
|
|
|
Stack<DdlTriggerContext>::AutoPushPop autoContext(attachment->ddlTriggersContext, context);
|
|
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
|
|
|
|
EXE_execute_ddl_triggers(tdbb, transaction, when == DTW_BEFORE, action);
|
|
|
|
|
|
|
|
savePoint.release(); // everything is ok
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DdlNode::executeDdlTrigger(thread_db* tdbb, jrd_tra* transaction,
|
|
|
|
DdlNode::DdlTriggerWhen when, int action, const MetaName& objectName)
|
|
|
|
{
|
|
|
|
executeDdlTrigger(tdbb, transaction, when, action, objectName, sqlText);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DdlNode::putType(const TypeClause& type, bool useSubType)
|
|
|
|
{
|
|
|
|
#ifdef DEV_BUILD
|
|
|
|
// Check if the field describes a known datatype
|
|
|
|
if (type.type > FB_NELEM(blr_dtypes) || !blr_dtypes[type.type])
|
|
|
|
{
|
|
|
|
SCHAR buffer[100];
|
|
|
|
|
|
|
|
sprintf(buffer, "Invalid dtype %d in put_dtype", type.type);
|
|
|
|
ERRD_bugcheck(buffer);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (type.notNull)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_not_nullable);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
if (type.typeOfName.hasData())
|
|
|
|
{
|
|
|
|
if (type.typeOfTable.hasData())
|
|
|
|
{
|
|
|
|
if (type.collateSpecified)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_column_name2);
|
|
|
|
dsqlScratch->appendUChar(type.fullDomain ? blr_domain_full : blr_domain_type_of);
|
|
|
|
dsqlScratch->appendMetaString(type.typeOfTable.c_str());
|
|
|
|
dsqlScratch->appendMetaString(type.typeOfName.c_str());
|
|
|
|
dsqlScratch->appendUShort(type.textType);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_column_name);
|
|
|
|
dsqlScratch->appendUChar(type.fullDomain ? blr_domain_full : blr_domain_type_of);
|
|
|
|
dsqlScratch->appendMetaString(type.typeOfTable.c_str());
|
|
|
|
dsqlScratch->appendMetaString(type.typeOfName.c_str());
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (type.collateSpecified)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_domain_name2);
|
|
|
|
dsqlScratch->appendUChar(type.fullDomain ? blr_domain_full : blr_domain_type_of);
|
|
|
|
dsqlScratch->appendMetaString(type.typeOfName.c_str());
|
|
|
|
dsqlScratch->appendUShort(type.textType);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_domain_name);
|
|
|
|
dsqlScratch->appendUChar(type.fullDomain ? blr_domain_full : blr_domain_type_of);
|
|
|
|
dsqlScratch->appendMetaString(type.typeOfName.c_str());
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (type.type)
|
|
|
|
{
|
|
|
|
case dtype_cstring:
|
|
|
|
case dtype_text:
|
|
|
|
case dtype_varying:
|
|
|
|
case dtype_blob:
|
|
|
|
if (!useSubType)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_dtypes[type.type]);
|
2009-10-21 02:42:38 +02:00
|
|
|
else if (type.type == dtype_varying)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_varying2);
|
|
|
|
dsqlScratch->appendUShort(type.textType);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
else if (type.type == dtype_cstring)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_cstring2);
|
|
|
|
dsqlScratch->appendUShort(type.textType);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
else if (type.type == dtype_blob)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_blob2);
|
|
|
|
dsqlScratch->appendUShort(type.subType);
|
|
|
|
dsqlScratch->appendUShort(type.textType);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_text2);
|
|
|
|
dsqlScratch->appendUShort(type.textType);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (type.type == dtype_varying)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUShort(type.length - sizeof(USHORT));
|
2009-10-21 02:42:38 +02:00
|
|
|
else if (type.type != dtype_blob)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUShort(type.length);
|
2009-10-21 02:42:38 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_dtypes[type.type]);
|
2009-10-21 02:42:38 +02:00
|
|
|
if (DTYPE_IS_EXACT(type.type) || dtype_quad == type.type)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(type.scale);
|
2009-10-21 02:42:38 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DdlNode::resetContextStack()
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->context->clear();
|
|
|
|
dsqlScratch->contextNumber = 0;
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-07-06 02:49:33 +02:00
|
|
|
MetaName DdlNode::storeGlobalField(thread_db* tdbb, jrd_tra* transaction, const TypeClause& field,
|
|
|
|
MetaName& name)
|
2009-12-21 18:23:07 +01:00
|
|
|
{
|
2010-07-06 02:49:33 +02:00
|
|
|
bool endStore = false;
|
2009-12-21 18:23:07 +01:00
|
|
|
|
2010-07-06 02:49:33 +02:00
|
|
|
try
|
2009-12-21 18:23:07 +01:00
|
|
|
{
|
2010-07-06 02:49:33 +02:00
|
|
|
if (name.isEmpty())
|
|
|
|
DYN_UTIL_generate_field_name(tdbb, NULL, name);
|
2009-12-21 18:23:07 +01:00
|
|
|
|
2010-07-06 02:49:33 +02:00
|
|
|
AutoCacheRequest requestHandle(tdbb, drq_s_fld_src, DYN_REQUESTS);
|
2009-12-21 18:23:07 +01:00
|
|
|
|
2010-07-06 02:49:33 +02:00
|
|
|
STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
FLD IN RDB$FIELDS
|
2009-12-21 18:23:07 +01:00
|
|
|
{
|
2010-07-06 02:49:33 +02:00
|
|
|
FLD.RDB$SYSTEM_FLAG = 0;
|
|
|
|
strcpy(FLD.RDB$FIELD_NAME, name.c_str());
|
|
|
|
|
|
|
|
updateRdbFields(field,
|
|
|
|
FLD.RDB$FIELD_TYPE,
|
|
|
|
FLD.RDB$FIELD_LENGTH,
|
|
|
|
FLD.RDB$FIELD_SUB_TYPE.NULL, FLD.RDB$FIELD_SUB_TYPE,
|
|
|
|
FLD.RDB$FIELD_SCALE.NULL, FLD.RDB$FIELD_SCALE,
|
|
|
|
FLD.RDB$CHARACTER_SET_ID.NULL, FLD.RDB$CHARACTER_SET_ID,
|
|
|
|
FLD.RDB$CHARACTER_LENGTH.NULL, FLD.RDB$CHARACTER_LENGTH,
|
|
|
|
FLD.RDB$FIELD_PRECISION.NULL, FLD.RDB$FIELD_PRECISION,
|
|
|
|
FLD.RDB$COLLATION_ID.NULL, FLD.RDB$COLLATION_ID,
|
|
|
|
FLD.RDB$SEGMENT_LENGTH.NULL, FLD.RDB$SEGMENT_LENGTH);
|
2009-12-21 18:23:07 +01:00
|
|
|
|
2010-07-06 02:49:33 +02:00
|
|
|
endStore = true;
|
2009-12-31 15:58:12 +01:00
|
|
|
}
|
2010-07-06 02:49:33 +02:00
|
|
|
END_STORE
|
|
|
|
}
|
|
|
|
catch (const status_exception& ex)
|
|
|
|
{
|
|
|
|
// STORE RDB$FIELDS failed
|
|
|
|
rethrowMetaException(ex, ENCODE_ISC_MSG(13, DYN_MSG_FAC), endStore);
|
2009-12-21 18:23:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
2009-11-21 21:42:27 +01:00
|
|
|
TypeClause::TypeClause(dsql_fld* aLegacyField, const MetaName& aCollate)
|
2009-10-21 02:42:38 +02:00
|
|
|
: legacyField(aLegacyField),
|
2009-11-21 21:42:27 +01:00
|
|
|
collate(aCollate)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
void TypeClause::resolve(DsqlCompilerScratch* dsqlScratch)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DDL_resolve_intl_type(dsqlScratch, legacyField,
|
2009-11-21 21:42:27 +01:00
|
|
|
(collate.isEmpty() ? NULL : MAKE_cstring(collate.c_str())));
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
type = legacyField->fld_dtype;
|
|
|
|
length = legacyField->fld_length;
|
|
|
|
scale = legacyField->fld_scale;
|
|
|
|
subType = legacyField->fld_sub_type;
|
|
|
|
segLength = legacyField->fld_seg_length;
|
|
|
|
precision = legacyField->fld_precision;
|
|
|
|
charLength = legacyField->fld_character_length;
|
|
|
|
charSetId = legacyField->fld_character_set_id;
|
|
|
|
collationId = legacyField->fld_collation_id;
|
2009-11-21 21:42:27 +01:00
|
|
|
collateSpecified = collate.hasData();
|
2009-10-21 02:42:38 +02:00
|
|
|
textType = legacyField->fld_ttype;
|
|
|
|
fullDomain = legacyField->fld_full_domain;
|
|
|
|
notNull = legacyField->fld_not_nullable;
|
|
|
|
fieldSource = legacyField->fld_source;
|
|
|
|
|
|
|
|
if (legacyField->fld_type_of_table)
|
|
|
|
typeOfTable = legacyField->fld_type_of_table->str_data;
|
|
|
|
|
|
|
|
typeOfName = legacyField->fld_type_of_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TypeClause::print(string& text) const
|
|
|
|
{
|
|
|
|
text.printf("typeOfTable: '%s' typeOfName: '%s' notNull: %d fieldSource: '%s'",
|
|
|
|
typeOfTable.c_str(), typeOfName.c_str(), notNull, fieldSource.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
2010-06-15 18:07:58 +02:00
|
|
|
ParameterClause::ParameterClause(dsql_fld* field, const MetaName& aCollate, dsql_nod* dflt,
|
|
|
|
dsql_nod* aLegacyParameter)
|
2009-11-21 21:42:27 +01:00
|
|
|
: TypeClause(field, aCollate),
|
2009-10-21 02:42:38 +02:00
|
|
|
name(field->fld_name),
|
2010-06-15 18:07:58 +02:00
|
|
|
legacyDefault(dflt),
|
|
|
|
legacyParameter(aLegacyParameter)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ParameterClause::print(string& text) const
|
|
|
|
{
|
|
|
|
string s;
|
|
|
|
TypeClause::print(s);
|
|
|
|
text.printf("name: '%s' %s", name.c_str(), s.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
2009-04-26 12:24:44 +02:00
|
|
|
void AlterCharSetNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
2008-05-19 15:47:48 +02:00
|
|
|
{
|
|
|
|
text.printf(
|
2009-10-21 02:42:38 +02:00
|
|
|
"AlterCharSetNode\n"
|
|
|
|
" charSet: %s\n"
|
|
|
|
" defaultCollation: %s\n",
|
2008-05-19 15:47:48 +02:00
|
|
|
charSet.c_str(), defaultCollation.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void AlterCharSetNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
METD_drop_charset(transaction, charSet);
|
|
|
|
MET_dsql_cache_release(tdbb, SYM_intlsym_charset, charSet);
|
2008-05-19 15:47:48 +02:00
|
|
|
|
|
|
|
bool charSetFound = false;
|
|
|
|
bool collationFound = false;
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
AutoCacheRequest requestHandle1(tdbb, drq_m_charset, DYN_REQUESTS);
|
2008-05-19 15:47:48 +02:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
FOR (REQUEST_HANDLE requestHandle1 TRANSACTION_HANDLE transaction)
|
2008-05-19 15:47:48 +02:00
|
|
|
CS IN RDB$CHARACTER_SETS
|
2009-12-05 15:21:52 +01:00
|
|
|
WITH CS.RDB$CHARACTER_SET_NAME EQ charSet.c_str()
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2008-05-19 15:47:48 +02:00
|
|
|
charSetFound = true;
|
|
|
|
|
2009-12-05 15:21:52 +01:00
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_CHARACTER_SET, charSet);
|
2008-05-19 15:47:48 +02:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
AutoCacheRequest requestHandle2(tdbb, drq_l_collation, DYN_REQUESTS);
|
2008-05-19 15:47:48 +02:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction)
|
2008-05-19 15:47:48 +02:00
|
|
|
COLL IN RDB$COLLATIONS
|
|
|
|
WITH COLL.RDB$CHARACTER_SET_ID EQ CS.RDB$CHARACTER_SET_ID AND
|
2009-12-05 15:21:52 +01:00
|
|
|
COLL.RDB$COLLATION_NAME EQ defaultCollation.c_str()
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2008-05-19 15:47:48 +02:00
|
|
|
collationFound = true;
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2009-01-05 09:48:32 +01:00
|
|
|
END_FOR
|
2008-05-19 15:47:48 +02:00
|
|
|
|
|
|
|
if (collationFound)
|
|
|
|
{
|
|
|
|
MODIFY CS
|
|
|
|
CS.RDB$DEFAULT_COLLATE_NAME.NULL = FALSE;
|
2009-12-05 15:21:52 +01:00
|
|
|
strcpy(CS.RDB$DEFAULT_COLLATE_NAME, defaultCollation.c_str());
|
2009-01-05 09:48:32 +01:00
|
|
|
END_MODIFY
|
2008-05-19 15:47:48 +02:00
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2009-01-05 09:48:32 +01:00
|
|
|
END_FOR
|
2008-05-19 15:47:48 +02:00
|
|
|
|
|
|
|
if (!charSetFound)
|
2009-12-18 00:59:07 +01:00
|
|
|
{
|
2009-12-17 11:50:40 +01:00
|
|
|
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(isc_charset_not_found) << Arg::Str(charSet));
|
2009-12-18 00:59:07 +01:00
|
|
|
}
|
2008-05-19 15:47:48 +02:00
|
|
|
|
|
|
|
if (!collationFound)
|
|
|
|
{
|
2009-12-17 11:50:40 +01:00
|
|
|
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(isc_collation_not_found) << Arg::Str(defaultCollation) <<
|
2009-01-05 09:48:32 +01:00
|
|
|
Arg::Str(charSet));
|
2008-05-19 15:47:48 +02:00
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-05 15:21:52 +01:00
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_CHARACTER_SET, charSet);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
2009-10-30 11:43:42 +01:00
|
|
|
void CommentOnNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
text.printf(
|
|
|
|
"CommentOnNode\n"
|
|
|
|
" objType: %s\n"
|
|
|
|
" objName: %s\n"
|
|
|
|
" text: %s\n",
|
|
|
|
objType, objName.c_str(), text.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// select rdb$relation_name from rdb$relation_fields where rdb$field_name = 'RDB$DESCRIPTION';
|
|
|
|
// gives the list of objects that accept descriptions. At FB2 time, the only
|
|
|
|
// subobjects with descriptions are relation's fields and procedure's parameters.
|
|
|
|
void CommentOnNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
|
|
|
Attachment* attachment = transaction->tra_attachment;
|
|
|
|
|
2010-01-03 21:56:39 +01:00
|
|
|
const char* tableClause = NULL;
|
|
|
|
const char* columnClause = NULL;
|
|
|
|
const char* subColumnClause = NULL;
|
|
|
|
const char* addWhereClause = NULL;
|
2009-10-21 02:42:38 +02:00
|
|
|
Arg::StatusVector status;
|
|
|
|
|
|
|
|
switch (objType)
|
|
|
|
{
|
|
|
|
case ddl_database:
|
2010-01-03 21:56:39 +01:00
|
|
|
tableClause = "rdb$database";
|
2009-10-21 02:42:38 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ddl_domain:
|
2010-01-03 21:56:39 +01:00
|
|
|
tableClause = "rdb$fields";
|
|
|
|
columnClause = "rdb$field_name";
|
2009-10-21 02:42:38 +02:00
|
|
|
status << Arg::Gds(isc_dyn_domain_not_found);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ddl_relation:
|
|
|
|
if (subName.hasData())
|
|
|
|
{
|
2010-01-03 21:56:39 +01:00
|
|
|
tableClause = "rdb$relation_fields";
|
|
|
|
subColumnClause = "rdb$field_name";
|
2009-10-21 02:42:38 +02:00
|
|
|
status << Arg::Gds(isc_dyn_column_does_not_exist) <<
|
|
|
|
Arg::Str(subName) << Arg::Str(objName);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-01-03 21:56:39 +01:00
|
|
|
tableClause = "rdb$relations";
|
|
|
|
addWhereClause = "rdb$view_blr is null";
|
2009-10-21 02:42:38 +02:00
|
|
|
status << Arg::Gds(isc_dyn_table_not_found) << Arg::Str(objName);
|
|
|
|
}
|
2010-01-03 21:56:39 +01:00
|
|
|
columnClause = "rdb$relation_name";
|
2009-10-21 02:42:38 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ddl_view:
|
2010-01-03 21:56:39 +01:00
|
|
|
tableClause = "rdb$relations";
|
|
|
|
columnClause = "rdb$relation_name";
|
2009-10-21 02:42:38 +02:00
|
|
|
status << Arg::Gds(isc_dyn_view_not_found) << Arg::Str(objName);
|
2010-01-03 21:56:39 +01:00
|
|
|
addWhereClause = "rdb$view_blr is not null";
|
2009-10-21 02:42:38 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ddl_procedure:
|
|
|
|
if (subName.hasData())
|
|
|
|
{
|
2010-01-03 21:56:39 +01:00
|
|
|
tableClause = "rdb$procedure_parameters";
|
|
|
|
subColumnClause = "rdb$parameter_name";
|
2009-10-21 02:42:38 +02:00
|
|
|
status << Arg::Gds(isc_dyn_proc_param_not_found) <<
|
|
|
|
Arg::Str(subName) << Arg::Str(objName);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-01-03 21:56:39 +01:00
|
|
|
tableClause = "rdb$procedures";
|
2009-10-21 02:42:38 +02:00
|
|
|
status << Arg::Gds(isc_dyn_proc_not_found) << Arg::Str(objName);
|
|
|
|
}
|
|
|
|
|
2010-01-03 21:56:39 +01:00
|
|
|
addWhereClause = "rdb$package_name is null";
|
|
|
|
columnClause = "rdb$procedure_name";
|
2009-10-21 02:42:38 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ddl_trigger:
|
2010-01-03 21:56:39 +01:00
|
|
|
tableClause = "rdb$triggers";
|
|
|
|
columnClause = "rdb$trigger_name";
|
2009-10-21 02:42:38 +02:00
|
|
|
status << Arg::Gds(isc_dyn_trig_not_found) << Arg::Str(objName);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ddl_udf:
|
2010-01-03 21:56:39 +01:00
|
|
|
tableClause = "rdb$functions";
|
|
|
|
columnClause = "rdb$function_name";
|
|
|
|
addWhereClause = "rdb$package_name is null";
|
2009-10-21 02:42:38 +02:00
|
|
|
status << Arg::Gds(isc_dyn_func_not_found) << Arg::Str(objName);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ddl_blob_filter:
|
2010-01-03 21:56:39 +01:00
|
|
|
tableClause = "rdb$filters";
|
|
|
|
columnClause = "rdb$function_name";
|
2009-10-21 02:42:38 +02:00
|
|
|
status << Arg::Gds(isc_dyn_filter_not_found) << Arg::Str(objName);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ddl_exception:
|
2010-01-03 21:56:39 +01:00
|
|
|
tableClause = "rdb$exceptions";
|
|
|
|
columnClause = "rdb$exception_name";
|
2009-10-21 02:42:38 +02:00
|
|
|
status << Arg::Gds(isc_dyn_exception_not_found) << Arg::Str(objName);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ddl_generator:
|
2010-01-03 21:56:39 +01:00
|
|
|
tableClause = "rdb$generators";
|
|
|
|
columnClause = "rdb$generator_name";
|
2009-10-21 02:42:38 +02:00
|
|
|
status << Arg::Gds(isc_dyn_gen_not_found) << Arg::Str(objName);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ddl_index:
|
2010-01-03 21:56:39 +01:00
|
|
|
tableClause = "rdb$indices";
|
|
|
|
columnClause = "rdb$index_name";
|
2009-10-21 02:42:38 +02:00
|
|
|
status << Arg::Gds(isc_dyn_index_not_found) << Arg::Str(objName);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ddl_role:
|
2010-01-03 21:56:39 +01:00
|
|
|
tableClause = "rdb$roles";
|
|
|
|
columnClause = "rdb$role_name";
|
2009-10-21 02:42:38 +02:00
|
|
|
status << Arg::Gds(isc_dyn_role_not_found) << Arg::Str(objName);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ddl_charset:
|
2010-01-03 21:56:39 +01:00
|
|
|
tableClause = "rdb$character_sets";
|
|
|
|
columnClause = "rdb$character_set_name";
|
2009-10-21 02:42:38 +02:00
|
|
|
status << Arg::Gds(isc_dyn_charset_not_found) << Arg::Str(objName);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ddl_collation:
|
2010-01-03 21:56:39 +01:00
|
|
|
tableClause = "rdb$collations";
|
|
|
|
columnClause = "rdb$collation_name";
|
2009-10-21 02:42:38 +02:00
|
|
|
status << Arg::Gds(isc_dyn_collation_not_found) << Arg::Str(objName);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ddl_package:
|
2010-01-03 21:56:39 +01:00
|
|
|
tableClause = "rdb$packages";
|
|
|
|
columnClause = "rdb$package_name";
|
2009-12-16 10:34:25 +01:00
|
|
|
status << Arg::Gds(isc_dyn_package_not_found) << Arg::Str(objName);
|
|
|
|
break;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-17 11:50:40 +01:00
|
|
|
case ddl_schema:
|
2010-01-03 21:56:39 +01:00
|
|
|
tableClause = "rdb$schemas";
|
|
|
|
columnClause = "rdb$schema_name";
|
2009-12-17 11:50:40 +01:00
|
|
|
status << Arg::Gds(isc_dyn_schema_not_found) << Arg::Str(objName);
|
2009-10-21 02:42:38 +02:00
|
|
|
break;
|
|
|
|
|
2010-01-03 21:56:39 +01:00
|
|
|
default:
|
|
|
|
fb_assert(false);
|
|
|
|
return;
|
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-03 21:56:39 +01:00
|
|
|
TriStateType<string> description;
|
|
|
|
if (!text.isEmpty())
|
|
|
|
description = attachment->stringToMetaCharSet(tdbb, text, textCharSet);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-03 21:56:39 +01:00
|
|
|
PreparedStatement::Builder sql;
|
|
|
|
sql << "update" << tableClause << "set rdb$description =" << description;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-03 21:56:39 +01:00
|
|
|
if (columnClause)
|
|
|
|
{
|
|
|
|
sql << "where" << columnClause << "=" << objName;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-03 21:56:39 +01:00
|
|
|
if (subColumnClause)
|
|
|
|
sql << "and" << subColumnClause << "=" << subName;
|
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-03 21:56:39 +01:00
|
|
|
if (addWhereClause)
|
|
|
|
sql << "and" << addWhereClause;
|
2009-10-23 02:42:40 +02:00
|
|
|
|
2010-01-03 21:56:39 +01:00
|
|
|
AutoPreparedStatement ps(attachment->prepareStatement(tdbb, transaction, sql));
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-03 21:56:39 +01:00
|
|
|
if (ps->executeUpdate(tdbb, transaction) == 0)
|
|
|
|
status_exception::raise(status);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
2009-10-30 11:43:42 +01:00
|
|
|
void CreateAlterFunctionNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
text.printf(
|
|
|
|
"CreateAlterFunctionNode\n"
|
|
|
|
" name: '%s' create: %d alter: %d\n",
|
|
|
|
name.c_str(), create, alter);
|
|
|
|
|
|
|
|
if (external)
|
|
|
|
{
|
|
|
|
string s;
|
|
|
|
s.printf(" external -> name: '%s' engine: '%s'\n",
|
|
|
|
external->name.c_str(), external->engine.c_str());
|
|
|
|
text += s;
|
|
|
|
}
|
|
|
|
|
|
|
|
text += " Parameters:\n";
|
|
|
|
|
|
|
|
for (size_t i = 0; i < parameters.getCount(); ++i)
|
|
|
|
{
|
|
|
|
const ParameterClause& parameter = parameters[i];
|
|
|
|
|
|
|
|
string s;
|
|
|
|
parameter.print(s);
|
|
|
|
text += " " + s + "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DdlNode* CreateAlterFunctionNode::internalDsqlPass()
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-21 18:23:07 +01:00
|
|
|
DsqlCompiledStatement* const statement = dsqlScratch->getStatement();
|
2009-12-22 16:36:10 +01:00
|
|
|
statement->setBlockNode(this);
|
2009-12-21 18:23:07 +01:00
|
|
|
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_FUNCTION);
|
|
|
|
|
|
|
|
const dsql_nod* variables = localDeclList;
|
|
|
|
if (variables)
|
|
|
|
{
|
|
|
|
// insure that variable names do not duplicate parameter names
|
|
|
|
|
|
|
|
SortedArray<MetaName> names;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < parameters.getCount(); ++i)
|
|
|
|
{
|
|
|
|
ParameterClause& parameter = parameters[i];
|
|
|
|
names.add(parameter.name);
|
|
|
|
}
|
|
|
|
|
|
|
|
const dsql_nod* const* ptr = variables->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + variables->nod_count; ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
if ((*ptr)->nod_type == nod_def_field)
|
|
|
|
{
|
|
|
|
const dsql_fld* field = (dsql_fld*) (*ptr)->nod_arg[e_dfl_field];
|
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
|
|
|
|
|
|
|
if (names.exist(field->fld_name))
|
|
|
|
{
|
|
|
|
status_exception::raise(
|
2009-12-24 11:42:32 +01:00
|
|
|
Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
|
2009-12-21 18:23:07 +01:00
|
|
|
Arg::Gds(isc_dsql_var_conflict) <<
|
|
|
|
Arg::Str(field->fld_name));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
source.ltrim("\n\r\t ");
|
|
|
|
|
2010-06-15 18:07:58 +02:00
|
|
|
bool hasDefaultParams = false;
|
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
// compile default expressions
|
|
|
|
for (unsigned i = 0; i < parameters.getCount(); ++i)
|
|
|
|
{
|
|
|
|
ParameterClause& parameter = parameters[i];
|
|
|
|
|
|
|
|
if (parameter.legacyDefault)
|
|
|
|
{
|
2010-06-15 18:07:58 +02:00
|
|
|
hasDefaultParams = true;
|
2009-12-21 18:23:07 +01:00
|
|
|
parameter.legacyDefault->nod_arg[e_dft_default] =
|
|
|
|
PASS1_node(dsqlScratch, parameter.legacyDefault->nod_arg[e_dft_default]);
|
|
|
|
}
|
2010-06-15 18:07:58 +02:00
|
|
|
else if (hasDefaultParams)
|
|
|
|
{
|
|
|
|
// parameter without default value after parameters with default
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
|
|
|
|
Arg::Gds(isc_bad_default_value) <<
|
|
|
|
Arg::Gds(isc_invalid_clause) << Arg::Str("defaults must be last"));
|
|
|
|
}
|
2009-12-21 18:23:07 +01:00
|
|
|
}
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
for (unsigned i = 0; i < parameters.getCount(); ++i)
|
2009-12-20 22:01:10 +01:00
|
|
|
parameters[i].resolve(dsqlScratch);
|
|
|
|
|
|
|
|
returnType.resolve(dsqlScratch);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
return DdlNode::internalDsqlPass();
|
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
|
|
|
|
void CreateAlterFunctionNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
fb_assert(create || alter);
|
|
|
|
|
|
|
|
// run all statements under savepoint control
|
|
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
2009-12-21 18:23:07 +01:00
|
|
|
bool altered = false;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
// first pass
|
2009-10-21 02:42:38 +02:00
|
|
|
if (alter)
|
|
|
|
{
|
2009-12-21 18:23:07 +01:00
|
|
|
if (executeAlter(tdbb, transaction, false, true))
|
|
|
|
{
|
|
|
|
altered = true;
|
|
|
|
}
|
|
|
|
else
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
if (create) // create or alter
|
2009-12-21 18:23:07 +01:00
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
executeCreate(tdbb, transaction);
|
2009-12-21 18:23:07 +01:00
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
2009-12-17 11:50:40 +01:00
|
|
|
Arg::Gds(isc_dyn_func_not_found) << Arg::Str(name));
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
}
|
2009-12-21 18:23:07 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
executeCreate(tdbb, transaction);
|
|
|
|
}
|
|
|
|
|
|
|
|
compile(tdbb, transaction);
|
|
|
|
|
|
|
|
executeAlter(tdbb, transaction, true, false); // second pass
|
|
|
|
|
|
|
|
if (package.isEmpty())
|
|
|
|
{
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER,
|
|
|
|
(altered ? DDL_TRIGGER_ALTER_FUNCTION : DDL_TRIGGER_CREATE_FUNCTION), name);
|
|
|
|
}
|
|
|
|
|
|
|
|
savePoint.release(); // everything is ok
|
|
|
|
|
|
|
|
if (alter)
|
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
// Update DSQL cache
|
2010-06-26 04:50:07 +02:00
|
|
|
METD_drop_function(transaction, QualifiedName(name, package));
|
|
|
|
MET_dsql_cache_release(tdbb, SYM_udf, name.c_str(), package);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CreateAlterFunctionNode::executeCreate(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
2009-12-06 20:11:25 +01:00
|
|
|
Attachment* attachment = transaction->getAttachment();
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
if (package.isEmpty())
|
2009-12-24 11:32:54 +01:00
|
|
|
{
|
2009-12-05 15:21:52 +01:00
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_FUNCTION, name);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-24 11:32:54 +01:00
|
|
|
DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_udf);
|
|
|
|
}
|
2009-12-25 20:29:58 +01:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
AutoCacheRequest requestHandle(tdbb, drq_s_funcs2, DYN_REQUESTS);
|
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
int faults = 0;
|
|
|
|
|
|
|
|
while (true)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-21 18:23:07 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
SINT64 id = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_fun_id, "RDB$FUNCTIONS");
|
|
|
|
id %= (MAX_SSHORT + 1);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
if (!id)
|
|
|
|
continue;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
FUN IN RDB$FUNCTIONS
|
|
|
|
{
|
|
|
|
FUN.RDB$FUNCTION_ID = id;
|
|
|
|
FUN.RDB$SYSTEM_FLAG = 0;
|
|
|
|
strcpy(FUN.RDB$FUNCTION_NAME, name.c_str());
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
FUN.RDB$LEGACY_FLAG.NULL = FALSE;
|
|
|
|
FUN.RDB$LEGACY_FLAG = 0;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
FUN.RDB$INVARIANT_FLAG.NULL = FALSE;
|
|
|
|
FUN.RDB$INVARIANT_FLAG = invariant ? TRUE : FALSE;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
FUN.RDB$RETURN_ARGUMENT.NULL = FALSE;
|
|
|
|
FUN.RDB$RETURN_ARGUMENT = 0;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
if (package.hasData())
|
|
|
|
{
|
|
|
|
FUN.RDB$PACKAGE_NAME.NULL = FALSE;
|
|
|
|
strcpy(FUN.RDB$PACKAGE_NAME, package.c_str());
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
FUN.RDB$PRIVATE_FLAG.NULL = FALSE;
|
|
|
|
FUN.RDB$PRIVATE_FLAG = privateScope;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
FUN.RDB$OWNER_NAME.NULL = FALSE;
|
|
|
|
strcpy(FUN.RDB$OWNER_NAME, packageOwner.c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FUN.RDB$PACKAGE_NAME.NULL = TRUE;
|
|
|
|
FUN.RDB$PRIVATE_FLAG.NULL = TRUE;
|
|
|
|
|
|
|
|
FUN.RDB$OWNER_NAME.NULL = FALSE;
|
|
|
|
strcpy(FUN.RDB$OWNER_NAME, attachment->att_user->usr_user_name.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
END_STORE
|
2009-12-22 01:08:49 +01:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
break;
|
|
|
|
}
|
2010-01-10 18:35:11 +01:00
|
|
|
catch (const status_exception& ex)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-21 18:23:07 +01:00
|
|
|
if (ex.value()[1] != isc_no_dup)
|
|
|
|
throw;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
if (++faults > MAX_SSHORT)
|
|
|
|
throw;
|
|
|
|
|
|
|
|
fb_utils::init_status(tdbb->tdbb_status_vector);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
if (package.isEmpty())
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-21 18:23:07 +01:00
|
|
|
for (const TEXT* p = ALL_PROC_PRIVILEGES; *p; p++)
|
|
|
|
{
|
|
|
|
requestHandle.reset(tdbb, drq_s_fun_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_udf;
|
|
|
|
X.RDB$PRIVILEGE[0] = *p;
|
|
|
|
X.RDB$PRIVILEGE[1] = 0;
|
|
|
|
}
|
|
|
|
END_STORE
|
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
executeAlter(tdbb, transaction, false, false);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
bool CreateAlterFunctionNode::executeAlter(thread_db* tdbb, jrd_tra* transaction,
|
|
|
|
bool secondPass, bool runTriggers)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-06 20:11:25 +01:00
|
|
|
Attachment* attachment = transaction->getAttachment();
|
2009-12-21 18:23:07 +01:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
bool modified = false;
|
|
|
|
|
|
|
|
AutoCacheRequest requestHandle(tdbb, drq_m_funcs2, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
FUN IN RDB$FUNCTIONS
|
2009-12-05 15:21:52 +01:00
|
|
|
WITH FUN.RDB$FUNCTION_NAME EQ name.c_str() AND
|
|
|
|
FUN.RDB$PACKAGE_NAME EQUIV NULLIF(package.c_str(), '')
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-17 11:50:40 +01:00
|
|
|
if (!FUN.RDB$SYSTEM_FLAG.NULL && FUN.RDB$SYSTEM_FLAG)
|
|
|
|
{
|
2009-12-22 01:08:49 +01:00
|
|
|
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(isc_dyn_cannot_mod_sysfunc) <<
|
|
|
|
FUN.RDB$FUNCTION_NAME);
|
2009-12-17 11:50:40 +01:00
|
|
|
}
|
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
if (!secondPass && runTriggers && package.isEmpty())
|
2009-12-05 15:21:52 +01:00
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_FUNCTION, name);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
MODIFY FUN
|
|
|
|
if (secondPass)
|
|
|
|
{
|
|
|
|
FUN.RDB$FUNCTION_BLR.NULL = TRUE;
|
|
|
|
FUN.RDB$DEBUG_INFO.NULL = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FUN.RDB$ENGINE_NAME.NULL = TRUE;
|
|
|
|
FUN.RDB$ENTRYPOINT.NULL = TRUE;
|
|
|
|
FUN.RDB$FUNCTION_SOURCE.NULL = TRUE;
|
|
|
|
|
|
|
|
FUN.RDB$VALID_BLR.NULL = FALSE;
|
|
|
|
FUN.RDB$VALID_BLR = TRUE;
|
|
|
|
|
|
|
|
FUN.RDB$FUNCTION_SOURCE.NULL = !(source.hasData() && (external || package.isEmpty()));
|
|
|
|
if (!FUN.RDB$FUNCTION_SOURCE.NULL)
|
|
|
|
attachment->storeMetaDataBlob(tdbb, transaction, &FUN.RDB$FUNCTION_SOURCE, source);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (external)
|
|
|
|
{
|
|
|
|
if (!secondPass)
|
|
|
|
{
|
|
|
|
FUN.RDB$ENGINE_NAME.NULL = FALSE;
|
|
|
|
strcpy(FUN.RDB$ENGINE_NAME, external->engine.c_str());
|
|
|
|
|
|
|
|
if (external->name.length() >= sizeof(FUN.RDB$ENTRYPOINT))
|
|
|
|
{
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_arith_except) <<
|
|
|
|
Arg::Gds(isc_string_truncation));
|
|
|
|
}
|
|
|
|
|
|
|
|
FUN.RDB$ENTRYPOINT.NULL = (SSHORT) external->name.isEmpty();
|
|
|
|
strcpy(FUN.RDB$ENTRYPOINT, external->name.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (body)
|
|
|
|
{
|
|
|
|
if (secondPass)
|
|
|
|
{
|
|
|
|
FUN.RDB$FUNCTION_BLR.NULL = FALSE;
|
|
|
|
attachment->storeBinaryBlob(tdbb, transaction, &FUN.RDB$FUNCTION_BLR,
|
2010-07-06 02:49:33 +02:00
|
|
|
dsqlScratch->getBlrData());
|
2009-12-21 18:23:07 +01:00
|
|
|
|
|
|
|
FUN.RDB$DEBUG_INFO.NULL = FALSE;
|
|
|
|
attachment->storeBinaryBlob(tdbb, transaction, &FUN.RDB$DEBUG_INFO,
|
2010-07-06 02:49:33 +02:00
|
|
|
dsqlScratch->getDebugData());
|
2009-12-21 18:23:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (package.hasData())
|
|
|
|
{
|
|
|
|
FUN.RDB$PRIVATE_FLAG.NULL = FALSE;
|
|
|
|
FUN.RDB$PRIVATE_FLAG = privateScope;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FUN.RDB$PRIVATE_FLAG.NULL = TRUE;
|
|
|
|
}
|
|
|
|
END_MODIFY
|
|
|
|
|
|
|
|
modified = true;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
if (!secondPass && modified)
|
|
|
|
{
|
|
|
|
// delete all old arguments
|
|
|
|
DropFunctionNode::dropArguments(tdbb, transaction, name, package);
|
|
|
|
|
|
|
|
// and insert the new ones
|
|
|
|
|
2010-06-15 18:07:58 +02:00
|
|
|
ParameterClause returnParameter(returnType.legacyField, returnType.collate, NULL, NULL);
|
2009-12-21 18:23:07 +01:00
|
|
|
returnParameter.resolve(dsqlScratch);
|
|
|
|
storeArgument(tdbb, transaction, 0, returnParameter);
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < parameters.getCount(); ++i)
|
|
|
|
{
|
|
|
|
ParameterClause& parameter = parameters[i];
|
|
|
|
storeArgument(tdbb, transaction, i + 1, parameter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return modified;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CreateAlterFunctionNode::storeArgument(thread_db* tdbb, jrd_tra* transaction,
|
|
|
|
unsigned pos, const ParameterClause& parameter)
|
|
|
|
{
|
|
|
|
Attachment* attachment = transaction->getAttachment();
|
|
|
|
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
|
|
|
|
|
|
|
AutoCacheRequest requestHandle(tdbb, drq_s_func_args2, DYN_REQUESTS);
|
|
|
|
|
|
|
|
STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
ARG IN RDB$FUNCTION_ARGUMENTS
|
|
|
|
{
|
|
|
|
ARG.RDB$FUNCTION_NAME.NULL = FALSE;
|
|
|
|
strcpy(ARG.RDB$FUNCTION_NAME, name.c_str());
|
|
|
|
|
2009-12-31 15:58:12 +01:00
|
|
|
// Avoid the return with name INTERNAL_FIELD_NAME.
|
|
|
|
if (parameter.name.hasData() && pos != 0)
|
2009-12-21 18:23:07 +01:00
|
|
|
{
|
|
|
|
ARG.RDB$ARGUMENT_NAME.NULL = FALSE;
|
|
|
|
strcpy(ARG.RDB$ARGUMENT_NAME, parameter.name.c_str());
|
|
|
|
}
|
2009-12-22 01:08:49 +01:00
|
|
|
else
|
|
|
|
ARG.RDB$ARGUMENT_NAME.NULL = TRUE;
|
2009-12-21 18:23:07 +01:00
|
|
|
|
|
|
|
if (package.hasData())
|
|
|
|
{
|
|
|
|
ARG.RDB$PACKAGE_NAME.NULL = FALSE;
|
|
|
|
strcpy(ARG.RDB$PACKAGE_NAME, package.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
ARG.RDB$ARGUMENT_POSITION.NULL = FALSE;
|
|
|
|
ARG.RDB$ARGUMENT_POSITION = pos;
|
|
|
|
|
|
|
|
ARG.RDB$ARGUMENT_MECHANISM.NULL = TRUE;
|
|
|
|
ARG.RDB$NULL_FLAG.NULL = TRUE;
|
|
|
|
ARG.RDB$RELATION_NAME.NULL = TRUE;
|
|
|
|
ARG.RDB$FIELD_NAME.NULL = TRUE;
|
|
|
|
ARG.RDB$FIELD_SOURCE.NULL = TRUE;
|
|
|
|
ARG.RDB$DEFAULT_VALUE.NULL = TRUE;
|
|
|
|
ARG.RDB$DEFAULT_SOURCE.NULL = TRUE;
|
|
|
|
|
|
|
|
ARG.RDB$MECHANISM.NULL = TRUE;
|
|
|
|
ARG.RDB$FIELD_TYPE.NULL = TRUE;
|
|
|
|
ARG.RDB$FIELD_LENGTH.NULL = TRUE;
|
|
|
|
ARG.RDB$FIELD_PRECISION.NULL = TRUE;
|
|
|
|
ARG.RDB$FIELD_SCALE.NULL = TRUE;
|
|
|
|
ARG.RDB$CHARACTER_SET_ID.NULL = TRUE;
|
|
|
|
ARG.RDB$COLLATION_ID.NULL = TRUE;
|
|
|
|
|
2009-12-31 15:58:12 +01:00
|
|
|
ARG.RDB$ARGUMENT_MECHANISM.NULL = FALSE;
|
|
|
|
ARG.RDB$ARGUMENT_MECHANISM =
|
|
|
|
(USHORT) (parameter.fullDomain ? prm_mech_normal : prm_mech_type_of);
|
2009-12-21 18:23:07 +01:00
|
|
|
|
2009-12-31 15:58:12 +01:00
|
|
|
if (parameter.notNull)
|
|
|
|
{
|
|
|
|
ARG.RDB$NULL_FLAG.NULL = FALSE;
|
|
|
|
ARG.RDB$NULL_FLAG = TRUE;
|
|
|
|
}
|
2009-12-21 18:23:07 +01:00
|
|
|
|
2009-12-31 15:58:12 +01:00
|
|
|
ARG.RDB$FIELD_SOURCE.NULL = FALSE;
|
2009-12-21 18:23:07 +01:00
|
|
|
|
2009-12-31 15:58:12 +01:00
|
|
|
if (parameter.typeOfTable.isEmpty())
|
|
|
|
{
|
|
|
|
if (parameter.typeOfName.hasData())
|
|
|
|
strcpy(ARG.RDB$FIELD_SOURCE, parameter.typeOfName.c_str());
|
2009-12-21 18:23:07 +01:00
|
|
|
else
|
|
|
|
{
|
2010-07-06 02:49:33 +02:00
|
|
|
MetaName fieldName;
|
|
|
|
storeGlobalField(tdbb, transaction, parameter, fieldName);
|
2009-12-31 15:58:12 +01:00
|
|
|
strcpy(ARG.RDB$FIELD_SOURCE, fieldName.c_str());
|
2009-12-21 18:23:07 +01:00
|
|
|
}
|
|
|
|
}
|
2009-12-31 15:58:12 +01:00
|
|
|
else
|
2009-12-21 18:23:07 +01:00
|
|
|
{
|
2009-12-31 15:58:12 +01:00
|
|
|
ARG.RDB$RELATION_NAME.NULL = FALSE;
|
|
|
|
strcpy(ARG.RDB$RELATION_NAME, parameter.typeOfTable.c_str());
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-31 15:58:12 +01:00
|
|
|
ARG.RDB$FIELD_NAME.NULL = FALSE;
|
|
|
|
strcpy(ARG.RDB$FIELD_NAME, parameter.typeOfName.c_str());
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-31 15:58:12 +01:00
|
|
|
strcpy(ARG.RDB$FIELD_SOURCE, parameter.fieldSource.c_str());
|
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-31 15:58:12 +01:00
|
|
|
// ASF: If we used a collate with a domain or table.column type, write it
|
2010-01-01 21:23:52 +01:00
|
|
|
// into RDB$FUNCTION_ARGUMENTS.
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-31 15:58:12 +01:00
|
|
|
if (parameter.collateSpecified && parameter.typeOfName.hasData())
|
|
|
|
{
|
|
|
|
ARG.RDB$COLLATION_ID.NULL = FALSE;
|
|
|
|
ARG.RDB$COLLATION_ID = parameter.collationId;
|
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-31 15:58:12 +01:00
|
|
|
// ASF: I moved this block to write defaults on RDB$FUNCTION_ARGUMENTS.
|
|
|
|
// It was writing in RDB$FIELDS, but that would require special support
|
|
|
|
// for packaged functions signature verification.
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-31 15:58:12 +01:00
|
|
|
if (parameter.legacyDefault)
|
|
|
|
{
|
|
|
|
ARG.RDB$DEFAULT_VALUE.NULL = FALSE;
|
|
|
|
ARG.RDB$DEFAULT_SOURCE.NULL = FALSE;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-31 15:58:12 +01:00
|
|
|
dsql_str* defaultString =
|
|
|
|
(dsql_str*) parameter.legacyDefault->nod_arg[e_dft_default_source];
|
|
|
|
string defaultSource = string(defaultString->str_data, defaultString->str_length);
|
|
|
|
attachment->storeMetaDataBlob(tdbb, transaction, &ARG.RDB$DEFAULT_SOURCE, defaultSource);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->getBlrData().clear();
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-31 15:58:12 +01:00
|
|
|
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_version4);
|
2009-12-21 18:23:07 +01:00
|
|
|
else
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_version5);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-31 15:58:12 +01:00
|
|
|
GEN_expr(dsqlScratch, parameter.legacyDefault->nod_arg[e_dft_default]);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_eoc);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-31 15:58:12 +01:00
|
|
|
attachment->storeBinaryBlob(tdbb, transaction, &ARG.RDB$DEFAULT_VALUE,
|
2010-07-06 02:49:33 +02:00
|
|
|
dsqlScratch->getBlrData());
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
}
|
2009-12-21 18:23:07 +01:00
|
|
|
END_STORE
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-24 11:42:32 +01:00
|
|
|
void CreateAlterFunctionNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-21 18:23:07 +01:00
|
|
|
if (invalid)
|
2010-01-02 10:42:09 +01:00
|
|
|
status_exception::raise(Arg::Gds(isc_dyn_invalid_ddl_func) << name);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
if (compiled)
|
|
|
|
return;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
compiled = true;
|
|
|
|
invalid = true;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
if (body)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->beginDebug();
|
|
|
|
dsqlScratch->getBlrData().clear();
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-23 01:57:08 +01:00
|
|
|
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_version4);
|
2009-12-21 18:23:07 +01:00
|
|
|
else
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_version5);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_begin);
|
2009-12-21 18:23:07 +01:00
|
|
|
|
|
|
|
if (parameters.getCount() != 0)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-24 11:42:32 +01:00
|
|
|
fb_assert(parameters.getCount() < size_t(MAX_USHORT / 2));
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_message);
|
|
|
|
dsqlScratch->appendUChar(0);
|
|
|
|
dsqlScratch->appendUShort(2 * parameters.getCount());
|
2009-12-21 18:23:07 +01:00
|
|
|
|
|
|
|
for (unsigned i = 0; i < parameters.getCount(); ++i)
|
|
|
|
{
|
|
|
|
ParameterClause& parameter = parameters[i];
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->putDebugArgument(fb_dbg_arg_input, i,
|
2009-12-21 18:23:07 +01:00
|
|
|
parameter.name.c_str());
|
|
|
|
putType(parameter, true);
|
|
|
|
|
|
|
|
// add slot for null flag (parameter2)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_short);
|
|
|
|
dsqlScratch->appendUChar(0);
|
2009-12-21 18:23:07 +01:00
|
|
|
|
|
|
|
variables.add(MAKE_variable(parameter.legacyField,
|
|
|
|
parameter.name.c_str(), VAR_input, 0, (USHORT) (2 * i), 0));
|
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_message);
|
|
|
|
dsqlScratch->appendUChar(1);
|
|
|
|
dsqlScratch->appendUShort(2);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->putDebugArgument(fb_dbg_arg_output, 0, "");
|
2009-12-21 18:23:07 +01:00
|
|
|
putType(returnType, true);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
// add slot for null flag (parameter2)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_short);
|
|
|
|
dsqlScratch->appendUChar(0);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
dsql_nod* const var = MAKE_variable(returnType.legacyField, "", VAR_output, 1, 0, 0);
|
|
|
|
variables.add(var);
|
|
|
|
outputVariables.add(var);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
if (parameters.getCount() != 0)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_receive);
|
|
|
|
dsqlScratch->appendUChar(0);
|
2009-12-21 18:23:07 +01:00
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_begin);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
for (unsigned i = 0; i < parameters.getCount(); ++i)
|
|
|
|
{
|
|
|
|
ParameterClause& parameter = parameters[i];
|
|
|
|
|
|
|
|
if (parameter.fullDomain || parameter.notNull)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-21 18:23:07 +01:00
|
|
|
// ASF: To validate input parameters we need only to read its value.
|
|
|
|
// Assigning it to null is an easy way to do this.
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_assignment);
|
|
|
|
dsqlScratch->appendUChar(blr_parameter2);
|
|
|
|
dsqlScratch->appendUChar(0); // input
|
|
|
|
dsqlScratch->appendUShort(i * 2);
|
|
|
|
dsqlScratch->appendUShort(i * 2 + 1);
|
|
|
|
dsqlScratch->appendUChar(blr_null);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
dsql_var* const variable = (dsql_var*) outputVariables[0]->nod_arg[Dsql::e_var_variable];
|
2010-06-15 18:07:58 +02:00
|
|
|
putLocalVariable(dsqlScratch, variable, 0, NULL);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
// ASF: This is here to not change the old logic (proc_flag)
|
|
|
|
// of previous calls to PASS1_node and PASS1_statement.
|
|
|
|
dsqlScratch->setPsql(true);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-15 18:07:58 +02:00
|
|
|
putLocalVariables(dsqlScratch, localDeclList, 1);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_stall);
|
2009-12-21 18:23:07 +01:00
|
|
|
// put a label before body of procedure,
|
|
|
|
// so that any EXIT statement can get out
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_label);
|
|
|
|
dsqlScratch->appendUChar(0);
|
2009-12-21 18:23:07 +01:00
|
|
|
dsqlScratch->loopLevel = 0;
|
|
|
|
dsqlScratch->cursorNumber = 0;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
GEN_statement(dsqlScratch, PASS1_statement(dsqlScratch, body));
|
|
|
|
|
2009-12-23 01:57:08 +01:00
|
|
|
statement->setType(DsqlCompiledStatement::TYPE_DDL);
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_end);
|
|
|
|
genReturn(dsqlScratch, false);
|
2009-12-21 18:23:07 +01:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_end);
|
|
|
|
dsqlScratch->appendUChar(blr_eoc);
|
2009-12-21 18:23:07 +01:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->endDebug();
|
2009-12-21 18:23:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
invalid = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DropFunctionNode::dropArguments(thread_db* tdbb, jrd_tra* transaction,
|
2010-01-10 18:35:11 +01:00
|
|
|
const MetaName& functionName, const MetaName& packageName)
|
2009-12-21 18:23:07 +01:00
|
|
|
{
|
|
|
|
AutoCacheRequest requestHandle(tdbb, drq_e_func_args, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
ARG IN RDB$FUNCTION_ARGUMENTS
|
|
|
|
WITH ARG.RDB$FUNCTION_NAME EQ functionName.c_str() AND
|
|
|
|
ARG.RDB$PACKAGE_NAME EQUIV NULLIF(packageName.c_str(), '')
|
|
|
|
{
|
|
|
|
// get rid of arguments in rdb$fields
|
|
|
|
if (!ARG.RDB$FIELD_SOURCE.NULL)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-21 18:23:07 +01:00
|
|
|
AutoCacheRequest requestHandle2(tdbb, drq_e_arg_gfld, DYN_REQUESTS);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction)
|
|
|
|
FLD IN RDB$FIELDS
|
|
|
|
WITH FLD.RDB$FIELD_NAME EQ ARG.RDB$FIELD_SOURCE AND
|
|
|
|
FLD.RDB$FIELD_NAME STARTING WITH IMPLICIT_DOMAIN_PREFIX
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-21 18:23:07 +01:00
|
|
|
bool erase = true;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
AutoCacheRequest requestHandle3(tdbb, drq_e_arg_gfld2, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE requestHandle3 TRANSACTION_HANDLE transaction)
|
|
|
|
ARG2 IN RDB$FUNCTION_ARGUMENTS
|
|
|
|
WITH ARG2.RDB$FUNCTION_NAME = ARG.RDB$FUNCTION_NAME AND
|
|
|
|
ARG2.RDB$PACKAGE_NAME EQUIV
|
|
|
|
NULLIF(packageName.c_str(), '') AND
|
|
|
|
ARG2.RDB$ARGUMENT_NAME = ARG.RDB$ARGUMENT_NAME
|
|
|
|
{
|
|
|
|
if (!ARG2.RDB$RELATION_NAME.NULL && !ARG2.RDB$FIELD_NAME.NULL)
|
|
|
|
erase = false;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
if (erase)
|
|
|
|
ERASE FLD;
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2009-12-21 18:23:07 +01:00
|
|
|
END_FOR
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2009-12-21 18:23:07 +01:00
|
|
|
|
|
|
|
ERASE ARG;
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2009-12-21 18:23:07 +01:00
|
|
|
END_FOR
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-30 11:43:42 +01:00
|
|
|
void DropFunctionNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
text.printf(
|
|
|
|
"DropFunctionNode\n"
|
|
|
|
" name: '%s'\n",
|
|
|
|
name.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
DdlNode* DropFunctionNode::internalDsqlPass()
|
|
|
|
{
|
|
|
|
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_FUNCTION);
|
|
|
|
return DdlNode::internalDsqlPass();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
void DropFunctionNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
|
|
|
// run all statements under savepoint control
|
|
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
bool found = false;
|
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
dropArguments(tdbb, transaction, name, package);
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
AutoCacheRequest requestHandle(tdbb, drq_e_funcs, DYN_REQUESTS);
|
2009-10-23 02:42:40 +02:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
FUN IN RDB$FUNCTIONS
|
2009-12-05 15:21:52 +01:00
|
|
|
WITH FUN.RDB$FUNCTION_NAME EQ name.c_str() AND
|
|
|
|
FUN.RDB$PACKAGE_NAME EQUIV NULLIF(package.c_str(), '')
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
if (!FUN.RDB$SYSTEM_FLAG.NULL && FUN.RDB$SYSTEM_FLAG)
|
|
|
|
{
|
2009-12-17 11:50:40 +01:00
|
|
|
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(isc_dyn_cannot_mod_sysfunc) << FUN.RDB$FUNCTION_NAME);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (package.isEmpty())
|
2009-12-05 15:21:52 +01:00
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_FUNCTION, name);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
ERASE FUN;
|
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
if (!FUN.RDB$SECURITY_CLASS.NULL)
|
|
|
|
DYN_delete_security_class2(transaction, FUN.RDB$SECURITY_CLASS);
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
if (!found && !silent)
|
|
|
|
{
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
2009-12-17 11:50:40 +01:00
|
|
|
Arg::Gds(isc_dyn_func_not_found) << Arg::Str(name));
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
if (package.isEmpty())
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-21 18:23:07 +01:00
|
|
|
requestHandle.reset(tdbb, drq_e_fun_prvs, 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_udf
|
|
|
|
{
|
|
|
|
ERASE PRIV;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
requestHandle.reset(tdbb, drq_e_fun_prv, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
PRIV IN RDB$USER_PRIVILEGES WITH PRIV.RDB$USER EQ name.c_str()
|
|
|
|
AND PRIV.RDB$USER_TYPE = obj_udf
|
|
|
|
{
|
|
|
|
ERASE PRIV;
|
|
|
|
}
|
|
|
|
END_FOR
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
2009-10-23 16:41:40 +02:00
|
|
|
if (found && package.isEmpty())
|
2009-12-05 15:21:52 +01:00
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_DROP_FUNCTION, name);
|
2009-10-23 16:41:40 +02:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
savePoint.release(); // everything is ok
|
|
|
|
|
|
|
|
// Update DSQL cache
|
2010-06-26 04:50:07 +02:00
|
|
|
METD_drop_function(transaction, QualifiedName(name, package));
|
|
|
|
MET_dsql_cache_release(tdbb, SYM_udf, name.c_str(), package);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
2009-12-24 11:42:32 +01:00
|
|
|
void RecreateFunctionNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
2009-12-21 18:23:07 +01:00
|
|
|
{
|
|
|
|
text.printf("RecreateFunctionNode\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DdlNode* RecreateFunctionNode::internalDsqlPass()
|
|
|
|
{
|
|
|
|
createNode->dsqlPass(dsqlScratch);
|
|
|
|
dropNode.dsqlPass(dsqlScratch);
|
|
|
|
return DdlNode::internalDsqlPass();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RecreateFunctionNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
|
|
|
// run all statements under savepoint control
|
|
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
|
2010-03-06 20:10:48 +01:00
|
|
|
dropNode.executeDdl(tdbb, transaction);
|
|
|
|
createNode->executeDdl(tdbb, transaction);
|
2009-12-21 18:23:07 +01:00
|
|
|
|
|
|
|
savePoint.release(); // everything is ok
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
2009-10-30 11:43:42 +01:00
|
|
|
void CreateAlterProcedureNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
text.printf(
|
|
|
|
"CreateAlterProcedureNode\n"
|
|
|
|
" name: '%s' create: %d alter: %d\n",
|
|
|
|
name.c_str(), create, alter);
|
|
|
|
|
|
|
|
if (external)
|
|
|
|
{
|
|
|
|
string s;
|
|
|
|
s.printf(" external -> name: '%s' engine: '%s'\n",
|
|
|
|
external->name.c_str(), external->engine.c_str());
|
|
|
|
text += s;
|
|
|
|
}
|
|
|
|
|
|
|
|
text += " Parameters:\n";
|
|
|
|
|
|
|
|
for (size_t i = 0; i < parameters.getCount(); ++i)
|
|
|
|
{
|
|
|
|
const ParameterClause& parameter = parameters[i];
|
|
|
|
|
|
|
|
string s;
|
|
|
|
parameter.print(s);
|
|
|
|
text += " " + s + "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
text += " Returns:\n";
|
|
|
|
|
|
|
|
for (size_t i = 0; i < returns.getCount(); ++i)
|
|
|
|
{
|
|
|
|
const ParameterClause& parameter = returns[i];
|
|
|
|
|
|
|
|
string s;
|
|
|
|
parameter.print(s);
|
|
|
|
text += " " + s + "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DdlNode* CreateAlterProcedureNode::internalDsqlPass()
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
2009-12-22 16:36:10 +01:00
|
|
|
statement->setBlockNode(this);
|
2009-12-20 23:42:43 +01:00
|
|
|
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_PROCEDURE);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
const dsql_nod* variables = localDeclList;
|
|
|
|
if (variables)
|
|
|
|
{
|
|
|
|
// insure that variable names do not duplicate parameter names
|
|
|
|
|
2009-11-21 21:42:27 +01:00
|
|
|
SortedArray<MetaName> names;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < parameters.getCount(); ++i)
|
|
|
|
{
|
|
|
|
ParameterClause& parameter = parameters[i];
|
|
|
|
names.add(parameter.name);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < returns.getCount(); ++i)
|
|
|
|
{
|
|
|
|
ParameterClause& parameter = returns[i];
|
|
|
|
names.add(parameter.name);
|
|
|
|
}
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
const dsql_nod* const* ptr = variables->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + variables->nod_count; ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
if ((*ptr)->nod_type == nod_def_field)
|
|
|
|
{
|
|
|
|
const dsql_fld* field = (dsql_fld*) (*ptr)->nod_arg[e_dfl_field];
|
|
|
|
DEV_BLKCHK(field, dsql_type_fld);
|
|
|
|
|
2009-11-21 21:42:27 +01:00
|
|
|
if (names.exist(field->fld_name))
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-11-21 21:42:27 +01:00
|
|
|
status_exception::raise(
|
2009-12-24 11:42:32 +01:00
|
|
|
Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
|
2009-12-17 11:50:40 +01:00
|
|
|
Arg::Gds(isc_dsql_var_conflict) << Arg::Str(field->fld_name));
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
source.ltrim("\n\r\t ");
|
|
|
|
|
2010-06-15 18:07:58 +02:00
|
|
|
bool hasDefaultParams = false;
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
// compile default expressions
|
|
|
|
for (unsigned i = 0; i < parameters.getCount(); ++i)
|
|
|
|
{
|
|
|
|
ParameterClause& parameter = parameters[i];
|
|
|
|
|
|
|
|
if (parameter.legacyDefault)
|
|
|
|
{
|
2010-06-15 18:07:58 +02:00
|
|
|
hasDefaultParams = true;
|
2009-10-21 02:42:38 +02:00
|
|
|
parameter.legacyDefault->nod_arg[e_dft_default] =
|
2009-12-20 22:01:10 +01:00
|
|
|
PASS1_node(dsqlScratch, parameter.legacyDefault->nod_arg[e_dft_default]);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2010-06-15 18:07:58 +02:00
|
|
|
else if (hasDefaultParams)
|
|
|
|
{
|
|
|
|
// parameter without default value after parameters with default
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
|
|
|
|
Arg::Gds(isc_bad_default_value) <<
|
|
|
|
Arg::Gds(isc_invalid_clause) << Arg::Str("defaults must be last"));
|
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
for (unsigned i = 0; i < parameters.getCount(); ++i)
|
|
|
|
parameters[i].resolve(dsqlScratch);
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < returns.getCount(); ++i)
|
|
|
|
returns[i].resolve(dsqlScratch);
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
return DdlNode::internalDsqlPass();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CreateAlterProcedureNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
|
|
|
fb_assert(create || alter);
|
|
|
|
|
|
|
|
// run all statements under savepoint control
|
|
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
bool altered = false;
|
|
|
|
|
|
|
|
// first pass
|
|
|
|
if (alter)
|
|
|
|
{
|
|
|
|
if (executeAlter(tdbb, transaction, false, true))
|
|
|
|
altered = true;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (create) // create or alter
|
|
|
|
executeCreate(tdbb, transaction);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
2009-12-17 11:50:40 +01:00
|
|
|
Arg::Gds(isc_dyn_proc_not_found) << Arg::Str(name));
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
executeCreate(tdbb, transaction);
|
|
|
|
|
|
|
|
compile(tdbb, transaction);
|
|
|
|
|
|
|
|
executeAlter(tdbb, transaction, true, false); // second pass
|
|
|
|
|
|
|
|
if (package.isEmpty())
|
|
|
|
{
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER,
|
2009-12-05 15:21:52 +01:00
|
|
|
(altered ? DDL_TRIGGER_ALTER_PROCEDURE : DDL_TRIGGER_CREATE_PROCEDURE), name);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
savePoint.release(); // everything is ok
|
|
|
|
|
|
|
|
if (alter)
|
|
|
|
{
|
|
|
|
// Update DSQL cache
|
2010-06-26 04:50:07 +02:00
|
|
|
METD_drop_procedure(transaction, QualifiedName(name, package));
|
|
|
|
MET_dsql_cache_release(tdbb, SYM_procedure, name.c_str(), package);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CreateAlterProcedureNode::executeCreate(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
2009-12-06 20:11:25 +01:00
|
|
|
Attachment* attachment = transaction->getAttachment();
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
if (package.isEmpty())
|
|
|
|
{
|
2009-12-05 15:21:52 +01:00
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_PROCEDURE, name);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-05 15:21:52 +01:00
|
|
|
DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_procedure);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
AutoCacheRequest requestHandle(tdbb, drq_s_prcs2, DYN_REQUESTS);
|
|
|
|
|
|
|
|
int faults = 0;
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
SINT64 id = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_prc_id, "RDB$PROCEDURES");
|
|
|
|
id %= (MAX_SSHORT + 1);
|
|
|
|
|
|
|
|
if (!id)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
P IN RDB$PROCEDURES
|
|
|
|
{
|
|
|
|
P.RDB$PROCEDURE_ID = id;
|
|
|
|
P.RDB$SYSTEM_FLAG = 0;
|
2009-12-05 15:21:52 +01:00
|
|
|
strcpy(P.RDB$PROCEDURE_NAME, name.c_str());
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
if (package.hasData())
|
|
|
|
{
|
|
|
|
P.RDB$PACKAGE_NAME.NULL = FALSE;
|
2009-12-05 15:21:52 +01:00
|
|
|
strcpy(P.RDB$PACKAGE_NAME, package.c_str());
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
P.RDB$PRIVATE_FLAG.NULL = FALSE;
|
|
|
|
P.RDB$PRIVATE_FLAG = privateScope;
|
|
|
|
|
|
|
|
strcpy(P.RDB$OWNER_NAME, packageOwner.c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
P.RDB$PACKAGE_NAME.NULL = TRUE;
|
|
|
|
P.RDB$PRIVATE_FLAG.NULL = TRUE;
|
|
|
|
|
|
|
|
strcpy(P.RDB$OWNER_NAME, attachment->att_user->usr_user_name.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
END_STORE
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2010-01-10 18:35:11 +01:00
|
|
|
catch (const status_exception& ex)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
if (ex.value()[1] != isc_no_dup)
|
|
|
|
throw;
|
|
|
|
|
|
|
|
if (++faults > MAX_SSHORT)
|
|
|
|
throw;
|
|
|
|
|
|
|
|
fb_utils::init_status(tdbb->tdbb_status_vector);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (package.isEmpty())
|
|
|
|
{
|
|
|
|
for (const TEXT* p = ALL_PROC_PRIVILEGES; *p; p++)
|
|
|
|
{
|
|
|
|
requestHandle.reset(tdbb, drq_s_prc_usr_prvs, DYN_REQUESTS);
|
|
|
|
|
|
|
|
STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
X IN RDB$USER_PRIVILEGES
|
|
|
|
{
|
2009-12-05 15:21:52 +01:00
|
|
|
strcpy(X.RDB$RELATION_NAME, name.c_str());
|
2009-10-21 02:42:38 +02:00
|
|
|
strcpy(X.RDB$USER, attachment->att_user->usr_user_name.c_str());
|
|
|
|
X.RDB$USER_TYPE = obj_user;
|
|
|
|
X.RDB$OBJECT_TYPE = obj_procedure;
|
|
|
|
X.RDB$PRIVILEGE[0] = *p;
|
|
|
|
X.RDB$PRIVILEGE[1] = 0;
|
|
|
|
}
|
|
|
|
END_STORE
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
executeAlter(tdbb, transaction, false, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, jrd_tra* transaction,
|
|
|
|
bool secondPass, bool runTriggers)
|
|
|
|
{
|
2009-12-06 20:11:25 +01:00
|
|
|
Attachment* attachment = transaction->getAttachment();
|
2009-10-21 02:42:38 +02:00
|
|
|
AutoCacheRequest requestHandle(tdbb, drq_m_prcs2, DYN_REQUESTS);
|
|
|
|
bool modified = false;
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
P IN RDB$PROCEDURES
|
2009-12-05 15:21:52 +01:00
|
|
|
WITH P.RDB$PROCEDURE_NAME EQ name.c_str() AND
|
|
|
|
P.RDB$PACKAGE_NAME EQUIV NULLIF(package.c_str(), '')
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
if (!P.RDB$SYSTEM_FLAG.NULL && P.RDB$SYSTEM_FLAG)
|
|
|
|
{
|
2009-12-17 11:50:40 +01:00
|
|
|
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(isc_dyn_cannot_mod_sysproc) << P.RDB$PROCEDURE_NAME);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!secondPass && runTriggers && package.isEmpty())
|
2009-12-05 15:21:52 +01:00
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_PROCEDURE, name);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
MODIFY P
|
|
|
|
if (secondPass)
|
|
|
|
{
|
|
|
|
P.RDB$PROCEDURE_BLR.NULL = TRUE;
|
|
|
|
P.RDB$DEBUG_INFO.NULL = TRUE;
|
|
|
|
P.RDB$PROCEDURE_TYPE.NULL = TRUE;
|
|
|
|
|
|
|
|
P.RDB$PROCEDURE_INPUTS = (USHORT) parameters.getCount();
|
|
|
|
P.RDB$PROCEDURE_OUTPUTS = (USHORT) returns.getCount();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
P.RDB$ENGINE_NAME.NULL = TRUE;
|
|
|
|
P.RDB$ENTRYPOINT.NULL = TRUE;
|
|
|
|
P.RDB$PROCEDURE_SOURCE.NULL = TRUE;
|
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
P.RDB$VALID_BLR = TRUE;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
P.RDB$PROCEDURE_SOURCE.NULL = !(source.hasData() && (external || package.isEmpty()));
|
|
|
|
if (!P.RDB$PROCEDURE_SOURCE.NULL)
|
2009-12-06 20:11:25 +01:00
|
|
|
attachment->storeMetaDataBlob(tdbb, transaction, &P.RDB$PROCEDURE_SOURCE, source);
|
2009-12-29 16:27:58 +01:00
|
|
|
|
|
|
|
if (package.hasData())
|
|
|
|
{
|
|
|
|
P.RDB$PRIVATE_FLAG.NULL = FALSE;
|
|
|
|
P.RDB$PRIVATE_FLAG = privateScope;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
P.RDB$PRIVATE_FLAG.NULL = TRUE;
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (external)
|
|
|
|
{
|
|
|
|
if (secondPass)
|
|
|
|
{
|
|
|
|
P.RDB$PROCEDURE_TYPE.NULL = FALSE;
|
|
|
|
P.RDB$PROCEDURE_TYPE = (USHORT) prc_selectable;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
P.RDB$ENGINE_NAME.NULL = FALSE;
|
2009-12-05 15:21:52 +01:00
|
|
|
strcpy(P.RDB$ENGINE_NAME, external->engine.c_str());
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
if (external->name.length() >= sizeof(P.RDB$ENTRYPOINT))
|
|
|
|
{
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_arith_except) <<
|
|
|
|
Arg::Gds(isc_string_truncation));
|
|
|
|
}
|
|
|
|
|
|
|
|
P.RDB$ENTRYPOINT.NULL = (SSHORT) external->name.isEmpty();
|
|
|
|
strcpy(P.RDB$ENTRYPOINT, external->name.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (body)
|
|
|
|
{
|
|
|
|
if (secondPass)
|
|
|
|
{
|
|
|
|
P.RDB$PROCEDURE_BLR.NULL = FALSE;
|
2009-12-07 06:32:08 +01:00
|
|
|
attachment->storeBinaryBlob(tdbb, transaction, &P.RDB$PROCEDURE_BLR,
|
2010-07-06 02:49:33 +02:00
|
|
|
dsqlScratch->getBlrData());
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
P.RDB$DEBUG_INFO.NULL = FALSE;
|
2009-12-07 06:32:08 +01:00
|
|
|
attachment->storeBinaryBlob(tdbb, transaction, &P.RDB$DEBUG_INFO,
|
2010-07-06 02:49:33 +02:00
|
|
|
dsqlScratch->getDebugData());
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
P.RDB$PROCEDURE_TYPE.NULL = FALSE;
|
2009-12-20 23:42:43 +01:00
|
|
|
P.RDB$PROCEDURE_TYPE = (USHORT)
|
2009-12-23 01:57:08 +01:00
|
|
|
(statement->getFlags() & DsqlCompiledStatement::FLAG_SELECTABLE ?
|
2009-12-20 23:42:43 +01:00
|
|
|
prc_selectable : prc_executable);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
END_MODIFY
|
|
|
|
|
|
|
|
modified = true;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
if (!secondPass && modified)
|
|
|
|
{
|
|
|
|
// delete all old input and output parameters
|
|
|
|
DropProcedureNode::dropParameters(tdbb, transaction, name, package);
|
|
|
|
|
|
|
|
// and insert the new ones
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < parameters.getCount(); ++i)
|
|
|
|
{
|
|
|
|
ParameterClause& parameter = parameters[i];
|
|
|
|
storeParameter(tdbb, transaction, 0, i, parameter);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < returns.getCount(); ++i)
|
|
|
|
{
|
|
|
|
ParameterClause& parameter = returns[i];
|
|
|
|
storeParameter(tdbb, transaction, 1, i, parameter);
|
|
|
|
}
|
2010-03-21 03:01:59 +01:00
|
|
|
|
|
|
|
AutoCacheRequest requestHandle2(tdbb, drq_m_prm_view, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction)
|
|
|
|
PRM IN RDB$PROCEDURE_PARAMETERS CROSS
|
|
|
|
RFR IN RDB$RELATION_FIELDS CROSS
|
|
|
|
VRL IN RDB$VIEW_RELATIONS
|
|
|
|
WITH PRM.RDB$PROCEDURE_NAME EQ name.c_str() AND
|
|
|
|
PRM.RDB$PACKAGE_NAME EQUIV NULLIF(package.c_str(), '') AND
|
|
|
|
VRL.RDB$RELATION_NAME EQ PRM.RDB$PROCEDURE_NAME AND
|
|
|
|
VRL.RDB$PACKAGE_NAME EQUIV PRM.RDB$PACKAGE_NAME AND
|
|
|
|
VRL.RDB$CONTEXT_TYPE EQ VCT_PROCEDURE AND
|
|
|
|
RFR.RDB$RELATION_NAME EQ VRL.RDB$VIEW_NAME AND
|
2010-03-21 03:27:54 +01:00
|
|
|
RFR.RDB$VIEW_CONTEXT EQ VRL.RDB$VIEW_CONTEXT AND
|
|
|
|
RFR.RDB$BASE_FIELD = PRM.RDB$PARAMETER_NAME
|
2010-03-21 03:01:59 +01:00
|
|
|
{
|
|
|
|
MODIFY RFR
|
|
|
|
{
|
|
|
|
strcpy(RFR.RDB$FIELD_SOURCE, PRM.RDB$FIELD_SOURCE);
|
|
|
|
}
|
|
|
|
END_MODIFY
|
|
|
|
}
|
|
|
|
END_FOR
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return modified;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CreateAlterProcedureNode::storeParameter(thread_db* tdbb, jrd_tra* transaction,
|
|
|
|
USHORT type, unsigned pos, const ParameterClause& parameter)
|
|
|
|
{
|
2009-12-06 20:11:25 +01:00
|
|
|
Attachment* attachment = transaction->getAttachment();
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
AutoCacheRequest requestHandle(tdbb, drq_s_prms4, DYN_REQUESTS);
|
|
|
|
|
|
|
|
STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
PRM IN RDB$PROCEDURE_PARAMETERS
|
|
|
|
{
|
|
|
|
PRM.RDB$PARAMETER_NAME.NULL = FALSE;
|
2009-12-05 15:21:52 +01:00
|
|
|
strcpy(PRM.RDB$PARAMETER_NAME, parameter.name.c_str());
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
PRM.RDB$PROCEDURE_NAME.NULL = FALSE;
|
2009-12-05 15:21:52 +01:00
|
|
|
strcpy(PRM.RDB$PROCEDURE_NAME, name.c_str());
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
if (package.hasData())
|
|
|
|
{
|
|
|
|
PRM.RDB$PACKAGE_NAME.NULL = FALSE;
|
2009-12-05 15:21:52 +01:00
|
|
|
strcpy(PRM.RDB$PACKAGE_NAME, package.c_str());
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
PRM.RDB$PACKAGE_NAME.NULL = TRUE;
|
|
|
|
|
|
|
|
PRM.RDB$SYSTEM_FLAG = 0;
|
|
|
|
PRM.RDB$SYSTEM_FLAG.NULL = FALSE;
|
|
|
|
|
|
|
|
PRM.RDB$PARAMETER_NUMBER.NULL = FALSE;
|
|
|
|
PRM.RDB$PARAMETER_NUMBER = pos;
|
|
|
|
|
|
|
|
PRM.RDB$PARAMETER_TYPE.NULL = FALSE;
|
|
|
|
PRM.RDB$PARAMETER_TYPE = type;
|
|
|
|
|
|
|
|
PRM.RDB$PARAMETER_MECHANISM.NULL = FALSE;
|
|
|
|
PRM.RDB$PARAMETER_MECHANISM =
|
|
|
|
(USHORT) (parameter.fullDomain ? prm_mech_normal : prm_mech_type_of);
|
|
|
|
|
|
|
|
PRM.RDB$NULL_FLAG.NULL = !parameter.notNull;
|
|
|
|
PRM.RDB$NULL_FLAG = parameter.notNull;
|
|
|
|
|
|
|
|
PRM.RDB$RELATION_NAME.NULL = parameter.typeOfTable.isEmpty();
|
|
|
|
PRM.RDB$FIELD_NAME.NULL = PRM.RDB$RELATION_NAME.NULL || parameter.typeOfName.isEmpty();
|
|
|
|
|
|
|
|
PRM.RDB$FIELD_SOURCE.NULL = FALSE;
|
|
|
|
|
|
|
|
if (PRM.RDB$RELATION_NAME.NULL)
|
|
|
|
{
|
|
|
|
if (parameter.typeOfName.hasData())
|
2009-12-05 15:21:52 +01:00
|
|
|
strcpy(PRM.RDB$FIELD_SOURCE, parameter.typeOfName.c_str());
|
2009-10-21 02:42:38 +02:00
|
|
|
else
|
|
|
|
{
|
2010-07-06 02:49:33 +02:00
|
|
|
MetaName fieldName;
|
|
|
|
storeGlobalField(tdbb, transaction, parameter, fieldName);
|
2009-12-21 18:23:07 +01:00
|
|
|
strcpy(PRM.RDB$FIELD_SOURCE, fieldName.c_str());
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-12-05 15:21:52 +01:00
|
|
|
strcpy(PRM.RDB$RELATION_NAME, parameter.typeOfTable.c_str());
|
|
|
|
strcpy(PRM.RDB$FIELD_NAME, parameter.typeOfName.c_str());
|
|
|
|
strcpy(PRM.RDB$FIELD_SOURCE, parameter.fieldSource.c_str());
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
2009-10-23 17:13:34 +02:00
|
|
|
// ASF: If we used a collate with a domain or table.column type, write it
|
|
|
|
// in RDB$PROCEDURE_PARAMETERS.
|
|
|
|
|
|
|
|
PRM.RDB$COLLATION_ID.NULL = !(parameter.collateSpecified && parameter.typeOfName.hasData());
|
|
|
|
|
|
|
|
if (!PRM.RDB$COLLATION_ID.NULL)
|
|
|
|
PRM.RDB$COLLATION_ID = parameter.collationId;
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
// ASF: I moved this block to write defaults on RDB$PROCEDURE_PARAMETERS.
|
|
|
|
// It was writing in RDB$FIELDS, but that would require special support
|
2009-10-23 02:42:40 +02:00
|
|
|
// for packaged procedures signature verification.
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
PRM.RDB$DEFAULT_VALUE.NULL = !parameter.legacyDefault;
|
|
|
|
PRM.RDB$DEFAULT_SOURCE.NULL = !parameter.legacyDefault;
|
|
|
|
|
|
|
|
if (parameter.legacyDefault)
|
|
|
|
{
|
|
|
|
dsql_str* defaultString =
|
|
|
|
(dsql_str*) parameter.legacyDefault->nod_arg[e_dft_default_source];
|
|
|
|
string defaultSource = string(defaultString->str_data, defaultString->str_length);
|
2009-12-06 20:11:25 +01:00
|
|
|
attachment->storeMetaDataBlob(tdbb, transaction, &PRM.RDB$DEFAULT_SOURCE, defaultSource);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->getBlrData().clear();
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-23 01:57:08 +01:00
|
|
|
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_version4);
|
2009-10-21 02:42:38 +02:00
|
|
|
else
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_version5);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
GEN_expr(dsqlScratch, parameter.legacyDefault->nod_arg[e_dft_default]);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_eoc);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-07 06:32:08 +01:00
|
|
|
attachment->storeBinaryBlob(tdbb, transaction, &PRM.RDB$DEFAULT_VALUE,
|
2010-07-06 02:49:33 +02:00
|
|
|
dsqlScratch->getBlrData());
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
END_STORE
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-30 11:43:42 +01:00
|
|
|
void CreateAlterProcedureNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
if (invalid)
|
2009-12-17 11:50:40 +01:00
|
|
|
status_exception::raise(Arg::Gds(isc_dyn_invalid_ddl_proc) << name);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
if (compiled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
compiled = true;
|
2009-10-24 20:38:25 +02:00
|
|
|
|
|
|
|
if (!body)
|
|
|
|
return;
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
invalid = true;
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->beginDebug();
|
|
|
|
dsqlScratch->getBlrData().clear();
|
2009-12-20 22:01:10 +01:00
|
|
|
|
2009-12-23 01:57:08 +01:00
|
|
|
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_version4);
|
2009-10-24 20:38:25 +02:00
|
|
|
else
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_version5);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_begin);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-10-24 20:38:25 +02:00
|
|
|
if (parameters.getCount() != 0)
|
|
|
|
{
|
2009-10-30 11:43:42 +01:00
|
|
|
fb_assert(parameters.getCount() < MAX_USHORT / 2);
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_message);
|
|
|
|
dsqlScratch->appendUChar(0);
|
|
|
|
dsqlScratch->appendUShort(2 * parameters.getCount());
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-10-24 20:38:25 +02:00
|
|
|
for (unsigned i = 0; i < parameters.getCount(); ++i)
|
|
|
|
{
|
|
|
|
ParameterClause& parameter = parameters[i];
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->putDebugArgument(fb_dbg_arg_input, i, parameter.name.c_str());
|
2009-10-24 20:38:25 +02:00
|
|
|
putType(parameter, true);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-10-24 20:38:25 +02:00
|
|
|
// add slot for null flag (parameter2)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_short);
|
|
|
|
dsqlScratch->appendUChar(0);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-11-21 21:42:27 +01:00
|
|
|
variables.add(MAKE_variable(parameter.legacyField,
|
|
|
|
parameter.name.c_str(), VAR_input, 0, (USHORT) (2 * i), 0));
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2009-10-24 20:38:25 +02:00
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-10-30 11:43:42 +01:00
|
|
|
fb_assert(returns.getCount() < MAX_USHORT / 2);
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_message);
|
|
|
|
dsqlScratch->appendUChar(1);
|
|
|
|
dsqlScratch->appendUShort(2 * returns.getCount() + 1);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-10-24 20:38:25 +02:00
|
|
|
if (returns.getCount() != 0)
|
|
|
|
{
|
|
|
|
for (unsigned i = 0; i < returns.getCount(); ++i)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-10-24 20:38:25 +02:00
|
|
|
ParameterClause& parameter = returns[i];
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->putDebugArgument(fb_dbg_arg_output, i, parameter.name.c_str());
|
2009-10-24 20:38:25 +02:00
|
|
|
putType(parameter, true);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-10-24 20:38:25 +02:00
|
|
|
// add slot for null flag (parameter2)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_short);
|
|
|
|
dsqlScratch->appendUChar(0);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
dsql_nod* const var = MAKE_variable(parameter.legacyField,
|
2009-10-24 20:38:25 +02:00
|
|
|
parameter.name.c_str(), VAR_output, 1, (USHORT) (2 * i), i);
|
2009-11-21 21:42:27 +01:00
|
|
|
|
|
|
|
variables.add(var);
|
|
|
|
outputVariables.add(var);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2009-10-24 20:38:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// add slot for EOS
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_short);
|
|
|
|
dsqlScratch->appendUChar(0);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-10-24 20:38:25 +02:00
|
|
|
if (parameters.getCount() != 0)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_receive);
|
|
|
|
dsqlScratch->appendUChar(0);
|
2009-10-24 20:38:25 +02:00
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_begin);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-10-24 20:38:25 +02:00
|
|
|
for (unsigned i = 0; i < parameters.getCount(); ++i)
|
|
|
|
{
|
|
|
|
ParameterClause& parameter = parameters[i];
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-10-24 20:38:25 +02:00
|
|
|
if (parameter.fullDomain || parameter.notNull)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-10-30 11:43:42 +01:00
|
|
|
// ASF: To validate an input parameter we need only to read its value.
|
2009-10-24 20:38:25 +02:00
|
|
|
// Assigning it to null is an easy way to do this.
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_assignment);
|
|
|
|
dsqlScratch->appendUChar(blr_parameter2);
|
|
|
|
dsqlScratch->appendUChar(0); // input
|
|
|
|
dsqlScratch->appendUShort(i * 2);
|
|
|
|
dsqlScratch->appendUShort(i * 2 + 1);
|
|
|
|
dsqlScratch->appendUChar(blr_null);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
2009-10-24 20:38:25 +02:00
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-11-21 21:42:27 +01:00
|
|
|
for (Array<dsql_nod*>::const_iterator i = outputVariables.begin(); i != outputVariables.end(); ++i)
|
2009-10-24 20:38:25 +02:00
|
|
|
{
|
2009-11-21 21:42:27 +01:00
|
|
|
dsql_nod* parameter = *i;
|
2009-12-21 18:23:07 +01:00
|
|
|
dsql_var* const variable = (dsql_var*) parameter->nod_arg[Dsql::e_var_variable];
|
2010-06-15 18:07:58 +02:00
|
|
|
putLocalVariable(dsqlScratch, variable, 0, NULL);
|
2009-10-24 20:38:25 +02:00
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-10-24 20:38:25 +02:00
|
|
|
// ASF: This is here to not change the old logic (proc_flag)
|
|
|
|
// of previous calls to PASS1_node and PASS1_statement.
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->setPsql(true);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-15 18:07:58 +02:00
|
|
|
putLocalVariables(dsqlScratch, localDeclList, returns.getCount());
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_stall);
|
2009-10-24 20:38:25 +02:00
|
|
|
// put a label before body of procedure,
|
|
|
|
// so that any EXIT statement can get out
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_label);
|
|
|
|
dsqlScratch->appendUChar(0);
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->loopLevel = 0;
|
|
|
|
dsqlScratch->cursorNumber = 0;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
GEN_statement(dsqlScratch, PASS1_statement(dsqlScratch, body));
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-23 01:57:08 +01:00
|
|
|
statement->setType(DsqlCompiledStatement::TYPE_DDL);
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_end);
|
|
|
|
genReturn(dsqlScratch, true);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_end);
|
|
|
|
dsqlScratch->appendUChar(blr_eoc);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->endDebug();
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
invalid = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
|
|
|
void DropProcedureNode::dropParameters(thread_db* tdbb, jrd_tra* transaction,
|
2010-01-10 18:35:11 +01:00
|
|
|
const MetaName& procedureName, const MetaName& packageName)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
AutoCacheRequest requestHandle(tdbb, drq_e_prms2, DYN_REQUESTS);
|
2009-10-23 02:42:40 +02:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
PRM IN RDB$PROCEDURE_PARAMETERS
|
2009-12-05 15:21:52 +01:00
|
|
|
WITH PRM.RDB$PROCEDURE_NAME EQ procedureName.c_str() AND
|
|
|
|
PRM.RDB$PACKAGE_NAME EQUIV NULLIF(packageName.c_str(), '')
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
// get rid of parameters in rdb$fields
|
2010-03-21 03:01:59 +01:00
|
|
|
if (!PRM.RDB$FIELD_SOURCE.NULL && PRM.RDB$RELATION_NAME.NULL && PRM.RDB$FIELD_NAME.NULL)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-21 18:23:07 +01:00
|
|
|
AutoCacheRequest requestHandle2(tdbb, drq_e_prm_gfld, DYN_REQUESTS);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction)
|
|
|
|
FLD IN RDB$FIELDS
|
|
|
|
WITH FLD.RDB$FIELD_NAME EQ PRM.RDB$FIELD_SOURCE AND
|
2009-11-26 16:54:54 +01:00
|
|
|
FLD.RDB$FIELD_NAME STARTING WITH IMPLICIT_DOMAIN_PREFIX AND
|
|
|
|
(FLD.RDB$SYSTEM_FLAG EQ 0 OR FLD.RDB$SYSTEM_FLAG MISSING)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2010-03-21 03:01:59 +01:00
|
|
|
ERASE FLD;
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
}
|
|
|
|
|
|
|
|
ERASE PRM;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-30 11:43:42 +01:00
|
|
|
void DropProcedureNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
text.printf(
|
|
|
|
"DropProcedureNode\n"
|
|
|
|
" name: '%s'\n",
|
|
|
|
name.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DdlNode* DropProcedureNode::internalDsqlPass()
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-20 23:42:43 +01:00
|
|
|
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_PROCEDURE);
|
2009-10-21 02:42:38 +02:00
|
|
|
return DdlNode::internalDsqlPass();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DropProcedureNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
|
|
|
// run all statements under savepoint control
|
|
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
bool found = false;
|
|
|
|
|
2009-12-05 15:21:52 +01:00
|
|
|
dropParameters(tdbb, transaction, name, package);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
AutoCacheRequest requestHandle(tdbb, drq_e_prcs2, DYN_REQUESTS);
|
2009-10-23 02:42:40 +02:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
PRC IN RDB$PROCEDURES
|
2009-12-05 15:21:52 +01:00
|
|
|
WITH PRC.RDB$PROCEDURE_NAME EQ name.c_str() AND
|
|
|
|
PRC.RDB$PACKAGE_NAME EQUIV NULLIF(package.c_str(), '')
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
if (!PRC.RDB$SYSTEM_FLAG.NULL && PRC.RDB$SYSTEM_FLAG)
|
|
|
|
{
|
2009-12-17 11:50:40 +01:00
|
|
|
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(isc_dyn_cannot_mod_sysproc) << PRC.RDB$PROCEDURE_NAME);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (package.isEmpty())
|
2009-12-05 15:21:52 +01:00
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_PROCEDURE, name);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
ERASE PRC;
|
|
|
|
|
|
|
|
if (!PRC.RDB$SECURITY_CLASS.NULL)
|
|
|
|
DYN_delete_security_class2(transaction, PRC.RDB$SECURITY_CLASS);
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
if (!found && !silent)
|
|
|
|
{
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
2009-12-17 11:50:40 +01:00
|
|
|
Arg::Gds(isc_dyn_proc_not_found) << Arg::Str(name));
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (package.isEmpty())
|
|
|
|
{
|
2009-12-21 18:23:07 +01:00
|
|
|
requestHandle.reset(tdbb, drq_e_prc_prvs, DYN_REQUESTS);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
2009-12-05 15:21:52 +01:00
|
|
|
PRIV IN RDB$USER_PRIVILEGES WITH PRIV.RDB$RELATION_NAME EQ name.c_str()
|
2009-10-21 02:42:38 +02:00
|
|
|
AND PRIV.RDB$OBJECT_TYPE = obj_procedure
|
|
|
|
{
|
|
|
|
ERASE PRIV;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
requestHandle.reset(tdbb, drq_e_prc_prv, DYN_REQUESTS);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
2009-12-05 15:21:52 +01:00
|
|
|
PRIV IN RDB$USER_PRIVILEGES WITH PRIV.RDB$USER EQ name.c_str()
|
2009-10-21 02:42:38 +02:00
|
|
|
AND PRIV.RDB$USER_TYPE = obj_procedure
|
|
|
|
{
|
|
|
|
ERASE PRIV;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
}
|
|
|
|
|
2009-10-23 16:41:40 +02:00
|
|
|
if (found && package.isEmpty())
|
2009-12-05 15:21:52 +01:00
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PROCEDURE, name);
|
2009-10-23 16:41:40 +02:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
savePoint.release(); // everything is ok
|
|
|
|
|
|
|
|
// Update DSQL cache
|
2010-06-26 04:50:07 +02:00
|
|
|
METD_drop_procedure(transaction, QualifiedName(name, package));
|
|
|
|
MET_dsql_cache_release(tdbb, SYM_procedure, name.c_str(), package);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
2009-10-30 11:43:42 +01:00
|
|
|
void RecreateProcedureNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
text.printf("RecreateProcedureNode\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DdlNode* RecreateProcedureNode::internalDsqlPass()
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
dropNode.dsqlPass(dsqlScratch);
|
|
|
|
createNode->dsqlPass(dsqlScratch);
|
2009-10-21 02:42:38 +02:00
|
|
|
return DdlNode::internalDsqlPass();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RecreateProcedureNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
|
|
|
// run all statements under savepoint control
|
|
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
|
2010-03-06 20:10:48 +01:00
|
|
|
dropNode.executeDdl(tdbb, transaction);
|
|
|
|
createNode->executeDdl(tdbb, transaction);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
savePoint.release(); // everything is ok
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
2009-10-30 11:43:42 +01:00
|
|
|
void CreateAlterTriggerNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
text.printf(
|
|
|
|
"CreateAlterTriggerNode\n"
|
|
|
|
" name: '%s' create: %d alter: %d relationName: '%s'\n"
|
|
|
|
" type: %d, %d active: %d, %d position: %d, %d\n",
|
|
|
|
name.c_str(), create, alter, relationName.c_str(),
|
|
|
|
type.specified, type.value, active.specified, active.value,
|
|
|
|
position.specified, position.value);
|
|
|
|
|
|
|
|
if (external)
|
|
|
|
{
|
|
|
|
string s;
|
|
|
|
s.printf(" external -> name: '%s' engine: '%s'\n",
|
|
|
|
external->name.c_str(), external->engine.c_str());
|
|
|
|
text += s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DdlNode* CreateAlterTriggerNode::internalDsqlPass()
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
2009-12-22 16:36:10 +01:00
|
|
|
statement->setBlockNode(this);
|
2009-12-21 18:23:07 +01:00
|
|
|
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_TRIGGER);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
if (type.specified)
|
|
|
|
{
|
|
|
|
if (create && // ALTER TRIGGER doesn't accept table name
|
|
|
|
((relationName.hasData() &&
|
|
|
|
(type.value & (unsigned) TRIGGER_TYPE_MASK) != (unsigned) TRIGGER_TYPE_DML) ||
|
|
|
|
(relationName.isEmpty() &&
|
|
|
|
(type.value & (unsigned) TRIGGER_TYPE_MASK) != (unsigned) TRIGGER_TYPE_DB &&
|
|
|
|
(type.value & (unsigned) TRIGGER_TYPE_MASK) != (unsigned) TRIGGER_TYPE_DDL)))
|
|
|
|
{
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_dsql_incompatible_trigger_type));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return DdlNode::internalDsqlPass();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CreateAlterTriggerNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
|
|
|
fb_assert(create || alter);
|
|
|
|
|
2009-12-06 20:11:25 +01:00
|
|
|
Attachment* attachment = transaction->getAttachment();
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
if (relationName.isEmpty() && !attachment->locksmith())
|
|
|
|
status_exception::raise(Arg::Gds(isc_adm_task_denied));
|
|
|
|
|
|
|
|
source.ltrim("\n\r\t ");
|
|
|
|
|
|
|
|
// run all statements under savepoint control
|
|
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
|
|
|
|
if (!create)
|
|
|
|
{
|
2010-04-20 00:49:18 +02:00
|
|
|
AutoRequest requestHandle;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
TRG IN RDB$TRIGGERS
|
2009-12-05 15:21:52 +01:00
|
|
|
WITH TRG.RDB$TRIGGER_NAME EQ name.c_str()
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
if (!type.specified && !TRG.RDB$TRIGGER_TYPE.NULL)
|
|
|
|
type = TRG.RDB$TRIGGER_TYPE;
|
|
|
|
|
|
|
|
if (relationName.isEmpty() && !TRG.RDB$RELATION_NAME.NULL)
|
|
|
|
relationName = TRG.RDB$RELATION_NAME;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
if (!type.specified)
|
|
|
|
{
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
2009-12-17 11:50:40 +01:00
|
|
|
Arg::Gds(isc_dyn_trig_not_found) << Arg::Str(name));
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
compile(tdbb, transaction);
|
|
|
|
|
|
|
|
if (alter)
|
|
|
|
{
|
|
|
|
if (!executeAlter(tdbb, transaction, true))
|
|
|
|
{
|
|
|
|
if (create) // create or alter
|
|
|
|
executeCreate(tdbb, transaction);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
2009-12-17 11:50:40 +01:00
|
|
|
Arg::Gds(isc_dyn_trig_not_found) << Arg::Str(name));
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
executeCreate(tdbb, transaction);
|
|
|
|
|
|
|
|
savePoint.release(); // everything is ok
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CreateAlterTriggerNode::executeCreate(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
2009-12-05 15:21:52 +01:00
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_TRIGGER, name);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
AutoCacheRequest requestHandle(tdbb, drq_s_triggers2, DYN_REQUESTS);
|
2010-01-10 17:39:56 +01:00
|
|
|
bool endStore = false;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-10 17:39:56 +01:00
|
|
|
try
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2010-01-10 17:39:56 +01:00
|
|
|
STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
TRG IN RDB$TRIGGERS
|
|
|
|
{
|
|
|
|
TRG.RDB$SYSTEM_FLAG = 0;
|
|
|
|
TRG.RDB$FLAGS = TRG_sql; // ASF: For FK triggers, TRG_ignore_perm will also be needed.
|
|
|
|
strcpy(TRG.RDB$TRIGGER_NAME, name.c_str());
|
|
|
|
|
|
|
|
TRG.RDB$RELATION_NAME.NULL = relationName.isEmpty();
|
|
|
|
strcpy(TRG.RDB$RELATION_NAME, relationName.c_str());
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-10 17:39:56 +01:00
|
|
|
fb_assert(type.specified);
|
|
|
|
TRG.RDB$TRIGGER_TYPE = type.value;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-10 17:39:56 +01:00
|
|
|
TRG.RDB$TRIGGER_SEQUENCE = (!position.specified ? 0 : position.value);
|
|
|
|
TRG.RDB$TRIGGER_INACTIVE = (!active.specified ? 0 : (USHORT) !active.value);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-10 17:39:56 +01:00
|
|
|
endStore = true;
|
|
|
|
}
|
|
|
|
END_STORE
|
|
|
|
}
|
|
|
|
catch (const status_exception& ex)
|
|
|
|
{
|
|
|
|
rethrowMetaException(ex, ENCODE_ISC_MSG(31, DYN_MSG_FAC), endStore); // DEFINE TRIGGER failed
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
executeAlter(tdbb, transaction, false);
|
|
|
|
|
2009-12-05 15:21:52 +01:00
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_TRIGGER, name);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool CreateAlterTriggerNode::executeAlter(thread_db* tdbb, jrd_tra* transaction, bool runTriggers)
|
|
|
|
{
|
2009-12-06 20:11:25 +01:00
|
|
|
Attachment* attachment = transaction->getAttachment();
|
2009-10-21 02:42:38 +02:00
|
|
|
bool modified = false;
|
|
|
|
|
|
|
|
// ASF: Unregistered bug (2.0, 2.1, 2.5, 3.0): CREATE OR ALTER TRIGGER accepts different table
|
|
|
|
// than one used in already created trigger.
|
|
|
|
|
|
|
|
AutoCacheRequest requestHandle(tdbb, drq_m_trigger2, DYN_REQUESTS);
|
|
|
|
|
2010-01-10 17:39:56 +01:00
|
|
|
try
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2010-01-10 17:39:56 +01:00
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
TRG IN RDB$TRIGGERS
|
|
|
|
WITH TRG.RDB$TRIGGER_NAME EQ name.c_str()
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2010-01-10 17:39:56 +01:00
|
|
|
if (type.specified && type.value != (FB_UINT64) TRG.RDB$TRIGGER_TYPE &&
|
|
|
|
((create && relationName.isEmpty()) || TRG.RDB$RELATION_NAME.NULL))
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2010-01-10 17:39:56 +01:00
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_dsql_db_trigger_type_cant_change));
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
2010-01-10 17:39:56 +01:00
|
|
|
if (!TRG.RDB$SYSTEM_FLAG.NULL)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2010-01-10 17:39:56 +01:00
|
|
|
switch (TRG.RDB$SYSTEM_FLAG)
|
|
|
|
{
|
|
|
|
case fb_sysflag_check_constraint:
|
|
|
|
case fb_sysflag_referential_constraint:
|
|
|
|
case fb_sysflag_view_check:
|
|
|
|
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(isc_dyn_cant_modify_auto_trig));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case fb_sysflag_system:
|
|
|
|
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(isc_dyn_cannot_mod_systrig) << TRG.RDB$TRIGGER_NAME);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
2010-01-10 17:39:56 +01:00
|
|
|
if (runTriggers)
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_TRIGGER, name);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-10 17:39:56 +01:00
|
|
|
MODIFY TRG
|
|
|
|
if (body || external)
|
|
|
|
{
|
|
|
|
fb_assert(!(body && external));
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-10 17:39:56 +01:00
|
|
|
TRG.RDB$ENGINE_NAME.NULL = TRUE;
|
|
|
|
TRG.RDB$ENTRYPOINT.NULL = TRUE;
|
|
|
|
TRG.RDB$TRIGGER_SOURCE.NULL = TRUE;
|
|
|
|
TRG.RDB$TRIGGER_BLR.NULL = TRUE;
|
|
|
|
TRG.RDB$DEBUG_INFO.NULL = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type.specified)
|
|
|
|
TRG.RDB$TRIGGER_TYPE = type.value;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-10 17:39:56 +01:00
|
|
|
if (position.specified)
|
|
|
|
TRG.RDB$TRIGGER_SEQUENCE = position.value;
|
|
|
|
if (active.specified)
|
|
|
|
TRG.RDB$TRIGGER_INACTIVE = (USHORT) !active.value;
|
|
|
|
|
|
|
|
if (external)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2010-01-10 17:39:56 +01:00
|
|
|
TRG.RDB$ENGINE_NAME.NULL = FALSE;
|
|
|
|
strcpy(TRG.RDB$ENGINE_NAME, external->engine.c_str());
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-10 17:39:56 +01:00
|
|
|
if (external->name.length() >= sizeof(TRG.RDB$ENTRYPOINT))
|
|
|
|
{
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_arith_except) <<
|
|
|
|
Arg::Gds(isc_string_truncation));
|
|
|
|
}
|
2009-12-20 22:01:10 +01:00
|
|
|
|
2010-01-10 17:39:56 +01:00
|
|
|
TRG.RDB$ENTRYPOINT.NULL = (SSHORT) external->name.isEmpty();
|
|
|
|
strcpy(TRG.RDB$ENTRYPOINT, external->name.c_str());
|
|
|
|
}
|
|
|
|
else if (body)
|
|
|
|
{
|
|
|
|
TRG.RDB$TRIGGER_BLR.NULL = FALSE;
|
|
|
|
attachment->storeBinaryBlob(tdbb, transaction, &TRG.RDB$TRIGGER_BLR,
|
2010-07-06 02:49:33 +02:00
|
|
|
dsqlScratch->getBlrData());
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-10 17:39:56 +01:00
|
|
|
TRG.RDB$DEBUG_INFO.NULL = FALSE;
|
|
|
|
attachment->storeBinaryBlob(tdbb, transaction, &TRG.RDB$DEBUG_INFO,
|
2010-07-06 02:49:33 +02:00
|
|
|
dsqlScratch->getDebugData());
|
2010-01-10 17:39:56 +01:00
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-10 17:39:56 +01:00
|
|
|
if (source.hasData())
|
|
|
|
{
|
|
|
|
TRG.RDB$TRIGGER_SOURCE.NULL = FALSE;
|
|
|
|
attachment->storeMetaDataBlob(tdbb, transaction, &TRG.RDB$TRIGGER_SOURCE, source);
|
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-01-10 17:39:56 +01:00
|
|
|
TRG.RDB$VALID_BLR = TRUE;
|
|
|
|
END_MODIFY
|
|
|
|
|
|
|
|
modified = true;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
}
|
|
|
|
catch (const status_exception& ex)
|
|
|
|
{
|
|
|
|
rethrowMetaException(ex, ENCODE_ISC_MSG(102, DYN_MSG_FAC), modified); // MODIFY TRIGGER failed
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (modified && runTriggers)
|
2009-12-05 15:21:52 +01:00
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_TRIGGER, name);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
return modified;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-30 11:43:42 +01:00
|
|
|
void CreateAlterTriggerNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
if (invalid)
|
2009-12-17 11:50:40 +01:00
|
|
|
status_exception::raise(Arg::Gds(isc_dyn_invalid_ddl_trig) << name);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
if (compiled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
compiled = true;
|
|
|
|
invalid = true;
|
|
|
|
|
|
|
|
if (body)
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->beginDebug();
|
|
|
|
dsqlScratch->getBlrData().clear();
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
// 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.
|
2009-12-20 22:01:10 +01:00
|
|
|
if (dsqlScratch->contextNumber)
|
2009-10-21 02:42:38 +02:00
|
|
|
resetContextStack();
|
|
|
|
|
|
|
|
if (relationName.hasData())
|
|
|
|
{
|
|
|
|
dsql_nod* relationNode = FB_NEW_RPT(getPool(), e_rln_count) dsql_nod;
|
|
|
|
///trigger_node->nod_arg[e_trg_table] = relationNode;
|
|
|
|
relationNode->nod_type = nod_relation_name;
|
|
|
|
relationNode->nod_count = e_rln_count;
|
|
|
|
relationNode->nod_arg[e_rln_name] = (dsql_nod*)
|
|
|
|
MAKE_string(relationName.c_str(), relationName.length());
|
|
|
|
|
|
|
|
dsql_nod* const temp = relationNode->nod_arg[e_rln_alias];
|
|
|
|
if (hasOldContext(type.value))
|
|
|
|
{
|
|
|
|
relationNode->nod_arg[e_rln_alias] = (dsql_nod*) MAKE_cstring(OLD_CONTEXT);
|
2009-12-20 22:01:10 +01:00
|
|
|
dsql_ctx* oldContext = PASS1_make_context(dsqlScratch, relationNode);
|
2009-10-21 02:42:38 +02:00
|
|
|
oldContext->ctx_flags |= CTX_system;
|
|
|
|
}
|
|
|
|
else
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->contextNumber++;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
if (hasNewContext(type.value))
|
|
|
|
{
|
|
|
|
relationNode->nod_arg[e_rln_alias] = (dsql_nod*) MAKE_cstring(NEW_CONTEXT);
|
2009-12-20 22:01:10 +01:00
|
|
|
dsql_ctx* newContext = PASS1_make_context(dsqlScratch, relationNode);
|
2009-10-21 02:42:38 +02:00
|
|
|
newContext->ctx_flags |= CTX_system;
|
|
|
|
}
|
|
|
|
else
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->contextNumber++;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
relationNode->nod_arg[e_rln_alias] = temp;
|
|
|
|
}
|
|
|
|
|
|
|
|
// generate the trigger blr
|
|
|
|
|
2009-12-23 01:57:08 +01:00
|
|
|
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_version4);
|
2009-10-21 02:42:38 +02:00
|
|
|
else
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_version5);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_begin);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->setPsql(true);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-15 18:07:58 +02:00
|
|
|
putLocalVariables(dsqlScratch, localDeclList, 0);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->scopeLevel++;
|
2009-10-21 02:42:38 +02:00
|
|
|
// dimitr: I see no reason to deny EXIT command in triggers,
|
|
|
|
// hence I've added zero label at the beginning.
|
|
|
|
// My first suspicion regarding an obvious conflict
|
|
|
|
// with trigger messages (nod_abort) is wrong,
|
|
|
|
// although the fact that they use the same BLR code
|
|
|
|
// is still a potential danger and must be fixed.
|
|
|
|
// Hopefully, system triggers are never recompiled.
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_label);
|
|
|
|
dsqlScratch->appendUChar(0);
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->loopLevel = 0;
|
|
|
|
dsqlScratch->cursorNumber = 0;
|
|
|
|
GEN_statement(dsqlScratch, PASS1_statement(dsqlScratch, body));
|
|
|
|
dsqlScratch->scopeLevel--;
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_end);
|
|
|
|
dsqlScratch->appendUChar(blr_eoc);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->endDebug();
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
// The statement type may have been set incorrectly when parsing
|
|
|
|
// the trigger actions, so reset it to reflect the fact that this
|
|
|
|
// is a data definition statement; also reset the ddl node.
|
2009-12-23 01:57:08 +01:00
|
|
|
statement->setType(DsqlCompiledStatement::TYPE_DDL);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
invalid = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
2009-10-30 11:43:42 +01:00
|
|
|
void DropTriggerNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
text.printf(
|
|
|
|
"DropTriggerNode\n"
|
|
|
|
" name: '%s'\n",
|
|
|
|
name.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DdlNode* DropTriggerNode::internalDsqlPass()
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-21 18:23:07 +01:00
|
|
|
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_TRIGGER);
|
2009-10-21 02:42:38 +02:00
|
|
|
return DdlNode::internalDsqlPass();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DropTriggerNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
|
|
|
// run all statements under savepoint control
|
|
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
|
|
|
|
bool found = false;
|
|
|
|
MetaName relationName;
|
|
|
|
AutoCacheRequest requestHandle(tdbb, drq_e_trigger3, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
X IN RDB$TRIGGERS
|
2009-12-05 15:21:52 +01:00
|
|
|
WITH X.RDB$TRIGGER_NAME EQ name.c_str()
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-17 11:50:40 +01:00
|
|
|
if (!X.RDB$SYSTEM_FLAG.NULL)
|
|
|
|
{
|
|
|
|
switch (X.RDB$SYSTEM_FLAG)
|
|
|
|
{
|
|
|
|
case fb_sysflag_check_constraint:
|
|
|
|
case fb_sysflag_referential_constraint:
|
|
|
|
case fb_sysflag_view_check:
|
|
|
|
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(isc_dyn_cant_modify_auto_trig));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case fb_sysflag_system:
|
|
|
|
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(isc_dyn_cannot_mod_systrig) << X.RDB$TRIGGER_NAME);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-06 20:11:25 +01:00
|
|
|
if (X.RDB$RELATION_NAME.NULL && !transaction->getAttachment()->locksmith())
|
2009-10-21 02:42:38 +02:00
|
|
|
status_exception::raise(Arg::Gds(isc_adm_task_denied));
|
|
|
|
|
2009-12-17 11:50:40 +01:00
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_TRIGGER, name);
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
relationName = X.RDB$RELATION_NAME;
|
|
|
|
ERASE X;
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
if (!found && !silent)
|
|
|
|
{
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
2009-12-17 11:50:40 +01:00
|
|
|
Arg::Gds(isc_dyn_trig_not_found) << Arg::Str(name));
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
requestHandle.reset(tdbb, drq_e_trg_msgs3, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
TM IN RDB$TRIGGER_MESSAGES
|
2009-12-05 15:21:52 +01:00
|
|
|
WITH TM.RDB$TRIGGER_NAME EQ name.c_str()
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
ERASE TM;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
requestHandle.reset(tdbb, drq_e_trg_prv2, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
PRIV IN RDB$USER_PRIVILEGES
|
2009-12-05 15:21:52 +01:00
|
|
|
WITH PRIV.RDB$USER EQ name.c_str() AND
|
2009-10-21 02:42:38 +02:00
|
|
|
PRIV.RDB$USER_TYPE = obj_trigger
|
|
|
|
{
|
|
|
|
ERASE PRIV;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
// Clear the update flags on the fields if this is the last remaining
|
|
|
|
// trigger that changes a view.
|
|
|
|
|
|
|
|
bool viewFound = false;
|
|
|
|
requestHandle.reset(tdbb, drq_e_trg_prv3, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
FIRST 1 V IN RDB$VIEW_RELATIONS
|
|
|
|
CROSS F IN RDB$RELATION_FIELDS
|
|
|
|
CROSS T IN RDB$TRIGGERS
|
|
|
|
WITH V.RDB$VIEW_NAME EQ relationName.c_str() AND
|
|
|
|
F.RDB$RELATION_NAME EQ V.RDB$VIEW_NAME AND
|
|
|
|
F.RDB$RELATION_NAME EQ T.RDB$RELATION_NAME
|
|
|
|
{
|
|
|
|
viewFound = true;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
if (!viewFound)
|
|
|
|
{
|
|
|
|
requestHandle.reset(tdbb, drq_m_rel_flds2, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
|
|
|
F IN RDB$RELATION_FIELDS
|
|
|
|
WITH F.RDB$RELATION_NAME EQ relationName.c_str()
|
|
|
|
{
|
|
|
|
MODIFY F USING
|
|
|
|
F.RDB$UPDATE_FLAG = FALSE;
|
|
|
|
END_MODIFY
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found)
|
2009-12-05 15:21:52 +01:00
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_DROP_TRIGGER, name);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
savePoint.release(); // everything is ok
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
2009-10-30 11:43:42 +01:00
|
|
|
void RecreateTriggerNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
text.printf("RecreateTriggerNode\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
DdlNode* RecreateTriggerNode::internalDsqlPass()
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
dropNode.dsqlPass(dsqlScratch);
|
|
|
|
createNode->dsqlPass(dsqlScratch);
|
2009-10-21 02:42:38 +02:00
|
|
|
return DdlNode::internalDsqlPass();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RecreateTriggerNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
|
|
|
// run all statements under savepoint control
|
|
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
|
2010-03-06 20:10:48 +01:00
|
|
|
dropNode.executeDdl(tdbb, transaction);
|
|
|
|
createNode->executeDdl(tdbb, transaction);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
savePoint.release(); // everything is ok
|
2008-05-19 15:47:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-10 18:32:40 +01:00
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
2010-06-26 03:52:06 +02:00
|
|
|
void CreateCollationNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
|
|
|
{
|
|
|
|
text.printf(
|
|
|
|
"CreateCollationNode\n"
|
|
|
|
" name: '%s'\n"
|
|
|
|
" forCharSet: '%s'\n"
|
|
|
|
" fromName: '%s'\n"
|
|
|
|
" fromExternal: '%s'\n"
|
|
|
|
" attributesOn: %x\n"
|
|
|
|
" attributesOff: %x\n",
|
|
|
|
name.c_str(), forCharSet.c_str(), fromName.c_str(), fromExternal.c_str(),
|
|
|
|
attributesOn, attributesOff);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CreateCollationNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
|
|
|
Attachment* attachment = transaction->tra_attachment;
|
|
|
|
|
|
|
|
// run all statements under savepoint control
|
|
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_COLLATION, name);
|
|
|
|
|
|
|
|
bool endStore = false;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
AutoCacheRequest request(tdbb, drq_s_colls, DYN_REQUESTS);
|
|
|
|
|
|
|
|
STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
|
|
X IN RDB$COLLATIONS
|
|
|
|
{
|
|
|
|
X.RDB$CHARACTER_SET_ID = forCharSetId;
|
|
|
|
strcpy(X.RDB$COLLATION_NAME, name.c_str());
|
|
|
|
X.RDB$SYSTEM_FLAG = 0;
|
|
|
|
|
|
|
|
X.RDB$SPECIFIC_ATTRIBUTES.NULL = TRUE;
|
|
|
|
X.RDB$BASE_COLLATION_NAME.NULL = TRUE;
|
|
|
|
|
|
|
|
CharSet* cs = INTL_charset_lookup(tdbb, forCharSetId);
|
|
|
|
SubtypeInfo info;
|
|
|
|
|
|
|
|
if (fromName.hasData())
|
|
|
|
{
|
|
|
|
if (MET_get_char_coll_subtype_info(tdbb,
|
|
|
|
INTL_CS_COLL_TO_TTYPE(forCharSetId, fromCollationId), &info) &&
|
|
|
|
info.specificAttributes.hasData())
|
|
|
|
{
|
|
|
|
UCharBuffer temp;
|
|
|
|
ULONG size = info.specificAttributes.getCount() * cs->maxBytesPerChar();
|
|
|
|
|
|
|
|
size = INTL_convert_bytes(tdbb, forCharSetId, temp.getBuffer(size), size,
|
|
|
|
CS_METADATA, info.specificAttributes.begin(),
|
|
|
|
info.specificAttributes.getCount(), status_exception::raise);
|
|
|
|
temp.shrink(size);
|
|
|
|
info.specificAttributes = temp;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(X.RDB$BASE_COLLATION_NAME, info.baseCollationName.c_str());
|
|
|
|
X.RDB$BASE_COLLATION_NAME.NULL = FALSE;
|
|
|
|
}
|
|
|
|
else if (fromExternal.hasData())
|
|
|
|
{
|
|
|
|
strcpy(X.RDB$BASE_COLLATION_NAME, fromExternal.c_str());
|
|
|
|
X.RDB$BASE_COLLATION_NAME.NULL = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (specificAttributes.hasData())
|
|
|
|
{
|
|
|
|
UCharBuffer temp;
|
|
|
|
ULONG size = specificAttributes.getCount() * cs->maxBytesPerChar();
|
|
|
|
|
|
|
|
size = INTL_convert_bytes(tdbb, forCharSetId, temp.getBuffer(size), size,
|
|
|
|
attachment->att_charset, specificAttributes.begin(),
|
|
|
|
specificAttributes.getCount(), status_exception::raise);
|
|
|
|
temp.shrink(size);
|
|
|
|
specificAttributes = temp;
|
|
|
|
}
|
|
|
|
|
|
|
|
info.charsetName = forCharSet.c_str();
|
|
|
|
info.collationName = name;
|
|
|
|
if (X.RDB$BASE_COLLATION_NAME.NULL)
|
|
|
|
info.baseCollationName = info.collationName;
|
|
|
|
else
|
|
|
|
info.baseCollationName = X.RDB$BASE_COLLATION_NAME;
|
|
|
|
info.ignoreAttributes = false;
|
|
|
|
|
|
|
|
if (!IntlManager::collationInstalled(info.baseCollationName.c_str(),
|
|
|
|
info.charsetName.c_str()))
|
|
|
|
{
|
|
|
|
// msg: 223: "Collation @1 not installed for character set @2"
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(ENCODE_ISC_MSG(223, DYN_MSG_FAC)) <<
|
|
|
|
info.baseCollationName << info.charsetName);
|
|
|
|
}
|
|
|
|
|
|
|
|
IntlUtil::SpecificAttributesMap map;
|
|
|
|
|
|
|
|
if (!IntlUtil::parseSpecificAttributes(
|
|
|
|
cs, info.specificAttributes.getCount(), info.specificAttributes.begin(), &map) ||
|
|
|
|
!IntlUtil::parseSpecificAttributes(
|
|
|
|
cs, specificAttributes.getCount(), specificAttributes.begin(), &map))
|
|
|
|
{
|
|
|
|
// msg: 222: "Invalid collation attributes"
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(ENCODE_ISC_MSG(222, DYN_MSG_FAC)));
|
|
|
|
}
|
|
|
|
|
|
|
|
const string s = IntlUtil::generateSpecificAttributes(cs, map);
|
|
|
|
string newSpecificAttributes;
|
|
|
|
|
|
|
|
if (!IntlManager::setupCollationAttributes(
|
|
|
|
info.baseCollationName.c_str(), info.charsetName.c_str(), s,
|
|
|
|
newSpecificAttributes))
|
|
|
|
{
|
|
|
|
// msg: 222: "Invalid collation attributes"
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(ENCODE_ISC_MSG(222, DYN_MSG_FAC)));
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(info.specificAttributes.getBuffer(newSpecificAttributes.length()),
|
|
|
|
newSpecificAttributes.begin(), newSpecificAttributes.length());
|
|
|
|
|
|
|
|
if (info.specificAttributes.hasData())
|
|
|
|
{
|
|
|
|
X.RDB$SPECIFIC_ATTRIBUTES.NULL = FALSE;
|
|
|
|
attachment->storeMetaDataBlob(tdbb, transaction, &X.RDB$SPECIFIC_ATTRIBUTES,
|
|
|
|
string(info.specificAttributes.begin(), info.specificAttributes.getCount()),
|
|
|
|
forCharSetId);
|
|
|
|
}
|
|
|
|
|
|
|
|
info.attributes = (info.attributes | attributesOn) & (~attributesOff);
|
|
|
|
X.RDB$COLLATION_ATTRIBUTES = info.attributes;
|
|
|
|
|
|
|
|
// Do not allow invalid attributes here.
|
|
|
|
if (!INTL_texttype_validate(tdbb, &info))
|
|
|
|
{
|
|
|
|
// msg: 222: "Invalid collation attributes"
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(ENCODE_ISC_MSG(222, DYN_MSG_FAC)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// ASF: User collations are created with the last number available,
|
|
|
|
// to minimize the possibility of conflicts with future system collations.
|
|
|
|
// The greater available number is 126 to avoid signed/unsigned problems.
|
|
|
|
|
|
|
|
X.RDB$COLLATION_ID.NULL = TRUE;
|
|
|
|
X.RDB$COLLATION_ID = 126;
|
|
|
|
|
|
|
|
AutoCacheRequest request2(tdbb, drq_l_max_coll_id, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR(REQUEST_HANDLE request2)
|
|
|
|
Y IN RDB$COLLATIONS
|
|
|
|
WITH Y.RDB$CHARACTER_SET_ID = forCharSetId AND
|
|
|
|
Y.RDB$COLLATION_ID NOT MISSING
|
|
|
|
SORTED BY DESCENDING Y.RDB$COLLATION_ID
|
|
|
|
{
|
|
|
|
if (Y.RDB$COLLATION_ID + 1 <= X.RDB$COLLATION_ID)
|
|
|
|
{
|
|
|
|
X.RDB$COLLATION_ID.NULL = FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
X.RDB$COLLATION_ID = Y.RDB$COLLATION_ID - 1;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
if (X.RDB$COLLATION_ID.NULL)
|
|
|
|
{
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(isc_max_coll_per_charset));
|
|
|
|
}
|
|
|
|
|
|
|
|
endStore = true;
|
|
|
|
}
|
|
|
|
END_STORE
|
|
|
|
}
|
|
|
|
catch (const status_exception& ex)
|
|
|
|
{
|
|
|
|
// DEFINE COLLATION failed
|
|
|
|
rethrowMetaException(ex, ENCODE_ISC_MSG(219, DYN_MSG_FAC), endStore);
|
|
|
|
}
|
|
|
|
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_COLLATION, name);
|
|
|
|
|
|
|
|
savePoint.release(); // everything is ok
|
|
|
|
|
|
|
|
// Update DSQL cache
|
2010-06-26 04:30:01 +02:00
|
|
|
METD_drop_collation(transaction, name);
|
|
|
|
MET_dsql_cache_release(tdbb, SYM_intlsym_collation, name.c_str());
|
2010-06-26 03:52:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
DdlNode* CreateCollationNode::internalDsqlPass()
|
|
|
|
{
|
|
|
|
const dsql_intlsym* resolvedCharSet = METD_get_charset(
|
|
|
|
dsqlScratch->getTransaction(), forCharSet.length(), forCharSet.c_str());
|
|
|
|
|
|
|
|
if (!resolvedCharSet)
|
|
|
|
{
|
|
|
|
// specified character set not found
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-504) <<
|
|
|
|
Arg::Gds(isc_charset_not_found) << forCharSet);
|
|
|
|
}
|
|
|
|
|
|
|
|
forCharSetId = resolvedCharSet->intlsym_charset_id;
|
|
|
|
|
|
|
|
if (fromName.hasData())
|
|
|
|
{
|
|
|
|
const dsql_intlsym* resolvedCollation = METD_get_collation(
|
2010-06-26 04:30:01 +02:00
|
|
|
dsqlScratch->getTransaction(), fromName, forCharSetId);
|
2010-06-26 03:52:06 +02:00
|
|
|
|
|
|
|
if (!resolvedCollation)
|
|
|
|
{
|
|
|
|
// Specified collation not found
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
|
|
|
|
Arg::Gds(isc_collation_not_found) << fromName << forCharSet);
|
|
|
|
}
|
|
|
|
|
|
|
|
fromCollationId = resolvedCollation->intlsym_collate_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
return DdlNode::internalDsqlPass();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
|
|
|
void DropCollationNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
|
|
|
{
|
|
|
|
text.printf(
|
|
|
|
"DropCollationNode\n"
|
|
|
|
" name: '%s'\n",
|
|
|
|
name.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void DropCollationNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
|
|
|
// run all statements under savepoint control
|
|
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
|
|
|
|
bool found = false;
|
|
|
|
AutoCacheRequest request(tdbb, drq_e_colls, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
|
|
COLL IN RDB$COLLATIONS
|
|
|
|
CROSS CS IN RDB$CHARACTER_SETS
|
|
|
|
WITH COLL.RDB$COLLATION_NAME EQ name.c_str() AND
|
|
|
|
CS.RDB$CHARACTER_SET_ID EQ COLL.RDB$CHARACTER_SET_ID
|
|
|
|
{
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_COLLATION, name);
|
|
|
|
|
|
|
|
if (!COLL.RDB$SYSTEM_FLAG.NULL && COLL.RDB$SYSTEM_FLAG)
|
|
|
|
{
|
|
|
|
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(isc_dyn_cannot_del_syscoll));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (COLL.RDB$COLLATION_ID == 0 ||
|
|
|
|
(!CS.RDB$DEFAULT_COLLATE_NAME.NULL &&
|
|
|
|
MetaName(COLL.RDB$COLLATION_NAME) == MetaName(CS.RDB$DEFAULT_COLLATE_NAME)))
|
|
|
|
{
|
|
|
|
fb_utils::exact_name_limit(CS.RDB$CHARACTER_SET_NAME,
|
|
|
|
sizeof(CS.RDB$CHARACTER_SET_NAME));
|
|
|
|
|
|
|
|
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(isc_dyn_cannot_del_def_coll) << CS.RDB$CHARACTER_SET_NAME);
|
|
|
|
}
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
fb_utils::exact_name_limit(COLL.RDB$COLLATION_NAME, sizeof(COLL.RDB$COLLATION_NAME));
|
|
|
|
|
|
|
|
AutoCacheRequest request2(tdbb, drq_l_rfld_coll, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction)
|
|
|
|
RF IN RDB$RELATION_FIELDS CROSS F IN RDB$FIELDS
|
|
|
|
WITH RF.RDB$FIELD_SOURCE EQ F.RDB$FIELD_NAME AND
|
|
|
|
F.RDB$CHARACTER_SET_ID EQ COLL.RDB$CHARACTER_SET_ID AND
|
|
|
|
RF.RDB$COLLATION_ID EQ COLL.RDB$COLLATION_ID
|
|
|
|
{
|
|
|
|
fb_utils::exact_name_limit(RF.RDB$RELATION_NAME, sizeof(RF.RDB$RELATION_NAME));
|
|
|
|
fb_utils::exact_name_limit(RF.RDB$FIELD_NAME, sizeof(RF.RDB$FIELD_NAME));
|
|
|
|
|
|
|
|
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(isc_dyn_coll_used_table) << COLL.RDB$COLLATION_NAME <<
|
|
|
|
RF.RDB$RELATION_NAME << RF.RDB$FIELD_NAME);
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
request2.reset(tdbb, drq_l_prm_coll, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction)
|
|
|
|
PRM IN RDB$PROCEDURE_PARAMETERS CROSS F IN RDB$FIELDS
|
|
|
|
WITH PRM.RDB$FIELD_SOURCE EQ F.RDB$FIELD_NAME AND
|
|
|
|
F.RDB$CHARACTER_SET_ID EQ COLL.RDB$CHARACTER_SET_ID AND
|
|
|
|
PRM.RDB$COLLATION_ID EQ COLL.RDB$COLLATION_ID
|
|
|
|
{
|
|
|
|
fb_utils::exact_name_limit(PRM.RDB$PARAMETER_NAME, sizeof(PRM.RDB$PARAMETER_NAME));
|
|
|
|
|
|
|
|
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(isc_dyn_coll_used_procedure) <<
|
|
|
|
COLL.RDB$COLLATION_NAME <<
|
|
|
|
QualifiedName(PRM.RDB$PROCEDURE_NAME,
|
|
|
|
(PRM.RDB$PACKAGE_NAME.NULL ? NULL : PRM.RDB$PACKAGE_NAME)).toString().c_str() <<
|
|
|
|
PRM.RDB$PARAMETER_NAME);
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
request2.reset(tdbb, drq_l_fld_coll, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction)
|
|
|
|
F IN RDB$FIELDS
|
|
|
|
WITH F.RDB$CHARACTER_SET_ID EQ COLL.RDB$CHARACTER_SET_ID AND
|
|
|
|
F.RDB$COLLATION_ID EQ COLL.RDB$COLLATION_ID
|
|
|
|
{
|
|
|
|
fb_utils::exact_name_limit(F.RDB$FIELD_NAME, sizeof(F.RDB$FIELD_NAME));
|
|
|
|
|
|
|
|
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(isc_dyn_coll_used_domain) << COLL.RDB$COLLATION_NAME << F.RDB$FIELD_NAME);
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
ERASE COLL;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
if (found)
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_DROP_COLLATION, name);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(isc_dyn_collation_not_found) << Arg::Str(name));
|
|
|
|
}
|
|
|
|
|
|
|
|
savePoint.release(); // everything is ok
|
|
|
|
|
|
|
|
// Update DSQL cache
|
2010-06-26 04:30:01 +02:00
|
|
|
METD_drop_collation(transaction, name);
|
|
|
|
MET_dsql_cache_release(tdbb, SYM_intlsym_collation, name.c_str());
|
2010-06-26 03:52:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
2010-07-06 02:49:33 +02:00
|
|
|
void CreateDomainNode::print(string& text, Array<dsql_nod*>& nodes) const
|
|
|
|
{
|
|
|
|
string nameTypeStr;
|
|
|
|
nameType.print(nameTypeStr);
|
|
|
|
|
|
|
|
text =
|
|
|
|
"CreateDomainNode\n"
|
|
|
|
" " + nameTypeStr + "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
void CreateDomainNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
|
|
|
Attachment* attachment = transaction->tra_attachment;
|
|
|
|
|
|
|
|
if (fb_utils::implicit_domain(nameType.name.c_str()))
|
|
|
|
{
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_sqlerr) << Arg::Num(-637) <<
|
|
|
|
Arg::Gds(isc_dsql_implicit_domain_name) << nameType.name);
|
|
|
|
}
|
|
|
|
|
|
|
|
const dsql_nod* elements = nameType.legacyField->fld_ranges;
|
|
|
|
const USHORT dims = elements ? elements->nod_count / 2 : 0;
|
|
|
|
|
|
|
|
if (nameType.legacyDefault && dims != 0)
|
|
|
|
{
|
|
|
|
// Default value is not allowed for array type in domain %s
|
|
|
|
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(ENCODE_ISC_MSG(226, DYN_MSG_FAC)) << nameType.name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dims > MAX_ARRAY_DIMENSIONS)
|
|
|
|
{
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_sqlerr) << Arg::Num(-604) <<
|
|
|
|
Arg::Gds(isc_dsql_max_arr_dim_exceeded));
|
|
|
|
}
|
|
|
|
|
|
|
|
nameType.resolve(dsqlScratch);
|
|
|
|
|
|
|
|
dsqlScratch->domainValue.dsc_dtype = nameType.type;
|
|
|
|
dsqlScratch->domainValue.dsc_length = nameType.length;
|
|
|
|
dsqlScratch->domainValue.dsc_scale = nameType.scale;
|
|
|
|
|
|
|
|
// run all statements under savepoint control
|
|
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_DOMAIN, nameType.name);
|
|
|
|
|
|
|
|
storeGlobalField(tdbb, transaction, nameType, nameType.name);
|
|
|
|
|
|
|
|
if (nameType.legacyDefault || check || notNull || dims != 0)
|
|
|
|
{
|
|
|
|
AutoCacheRequest request(tdbb, drq_m_fld, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
|
|
FLD IN RDB$FIELDS
|
|
|
|
WITH FLD.RDB$FIELD_NAME EQ nameType.name.c_str()
|
|
|
|
{
|
|
|
|
MODIFY FLD
|
|
|
|
if (nameType.legacyDefault)
|
|
|
|
{
|
|
|
|
dsql_str* defaultString =
|
|
|
|
(dsql_str*) nameType.legacyDefault->nod_arg[e_dft_default_source];
|
|
|
|
string defaultSource = string(defaultString->str_data, defaultString->str_length);
|
|
|
|
|
|
|
|
FLD.RDB$DEFAULT_SOURCE.NULL = FALSE;
|
|
|
|
attachment->storeMetaDataBlob(tdbb, transaction, &FLD.RDB$DEFAULT_SOURCE, defaultSource);
|
|
|
|
|
|
|
|
dsqlScratch->getBlrData().clear();
|
|
|
|
dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5);
|
|
|
|
|
|
|
|
dsql_nod* node = PASS1_node(dsqlScratch, nameType.legacyDefault->nod_arg[e_dft_default]);
|
|
|
|
|
|
|
|
GEN_hidden_variables(dsqlScratch, true);
|
|
|
|
GEN_expr(dsqlScratch, node);
|
|
|
|
|
|
|
|
dsqlScratch->appendUChar(blr_eoc);
|
|
|
|
|
|
|
|
FLD.RDB$DEFAULT_VALUE.NULL = FALSE;
|
|
|
|
attachment->storeBinaryBlob(tdbb, transaction, &FLD.RDB$DEFAULT_VALUE,
|
|
|
|
dsqlScratch->getBlrData());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (check)
|
|
|
|
{
|
|
|
|
dsql_str* checkString = (dsql_str*) check->nod_arg[e_cnstr_source];
|
|
|
|
string checkSource = string(checkString->str_data, checkString->str_length);
|
|
|
|
|
|
|
|
FLD.RDB$VALIDATION_SOURCE.NULL = FALSE;
|
|
|
|
attachment->storeMetaDataBlob(tdbb, transaction, &FLD.RDB$VALIDATION_SOURCE, checkSource);
|
|
|
|
|
|
|
|
dsqlScratch->getBlrData().clear();
|
|
|
|
dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5);
|
|
|
|
|
|
|
|
// Increment the context level for this statement, so that the context number for
|
|
|
|
// any RSE generated for a SELECT within the CHECK clause will be greater than 0.
|
|
|
|
// In the environment of a domain check constraint, context number 0 is reserved
|
|
|
|
// for the "blr_fid, 0, 0, 0," which is emitted for a nod_dom_value, corresponding
|
|
|
|
// to an occurance of the VALUE keyword in the body of the check constraint.
|
|
|
|
// -- chrisj 1999-08-20
|
|
|
|
++dsqlScratch->contextNumber;
|
|
|
|
|
|
|
|
dsql_nod* node = PASS1_node(dsqlScratch, check->nod_arg[e_cnstr_condition]);
|
|
|
|
|
|
|
|
GEN_hidden_variables(dsqlScratch, true);
|
|
|
|
GEN_expr(dsqlScratch, node);
|
|
|
|
|
|
|
|
dsqlScratch->appendUChar(blr_eoc);
|
|
|
|
|
|
|
|
FLD.RDB$VALIDATION_BLR.NULL = FALSE;
|
|
|
|
attachment->storeBinaryBlob(tdbb, transaction, &FLD.RDB$VALIDATION_BLR,
|
|
|
|
dsqlScratch->getBlrData());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (notNull)
|
|
|
|
{
|
|
|
|
FLD.RDB$NULL_FLAG.NULL = FALSE;
|
|
|
|
FLD.RDB$NULL_FLAG = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dims != 0)
|
|
|
|
{
|
|
|
|
FLD.RDB$DIMENSIONS.NULL = FALSE;
|
|
|
|
FLD.RDB$DIMENSIONS = dims;
|
|
|
|
}
|
|
|
|
END_MODIFY
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
}
|
|
|
|
|
|
|
|
if (elements) // Is the type an array?
|
|
|
|
{
|
|
|
|
AutoCacheRequest request(tdbb, drq_s_fld_dym, DYN_REQUESTS);
|
|
|
|
|
|
|
|
SSHORT position = 0;
|
|
|
|
const dsql_nod* const* ptr = elements->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + elements->nod_count; ptr < end; ++ptr, ++position)
|
|
|
|
{
|
|
|
|
const dsql_nod* element = *ptr++;
|
|
|
|
const SLONG lrange = element->getSlong();
|
|
|
|
element = *ptr;
|
|
|
|
const SLONG hrange = element->getSlong();
|
|
|
|
|
|
|
|
if (lrange >= hrange)
|
|
|
|
{
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_sqlerr) << Arg::Num(-604) <<
|
|
|
|
Arg::Gds(isc_dsql_arr_range_error));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool endStore = false;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
STORE (REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
|
|
DIM IN RDB$FIELD_DIMENSIONS
|
|
|
|
{
|
|
|
|
strcpy(DIM.RDB$FIELD_NAME, nameType.name.c_str());
|
|
|
|
DIM.RDB$DIMENSION = position;
|
|
|
|
DIM.RDB$UPPER_BOUND = hrange;
|
|
|
|
DIM.RDB$LOWER_BOUND = lrange;
|
|
|
|
|
|
|
|
endStore = true;
|
|
|
|
}
|
|
|
|
END_STORE
|
|
|
|
|
|
|
|
}
|
|
|
|
catch (const status_exception& ex)
|
|
|
|
{
|
|
|
|
// STORE RDB$FIELD_DIMENSIONS failed
|
|
|
|
rethrowMetaException(ex, ENCODE_ISC_MSG(3, DYN_MSG_FAC), endStore);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_DOMAIN, nameType.name);
|
|
|
|
|
|
|
|
savePoint.release(); // everything is ok
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
|
|
|
// Compare the original field type with the new field type to determine if the original type can be
|
|
|
|
// changed to the new type.
|
|
|
|
//
|
|
|
|
// The following conversions are not allowed:
|
|
|
|
// Blob to anything
|
|
|
|
// Array to anything
|
|
|
|
// Date to anything
|
|
|
|
// Char to any numeric
|
|
|
|
// Varchar to any numeric
|
|
|
|
// Anything to Blob
|
|
|
|
// Anything to Array
|
|
|
|
//
|
|
|
|
// This function throws an exception if the conversion can not be made.
|
|
|
|
//
|
|
|
|
// ASF: We should stop using dyn_fld here as soon DYN stops to be a caller of this function.
|
|
|
|
void AlterDomainNode::checkUpdate(const dyn_fld& origFld, const dyn_fld& newFld)
|
|
|
|
{
|
|
|
|
ULONG errorCode = FB_SUCCESS;
|
|
|
|
|
|
|
|
// Check to make sure that the old and new types are compatible
|
|
|
|
switch (origFld.dyn_dtype)
|
|
|
|
{
|
|
|
|
// CHARACTER types
|
|
|
|
case blr_text:
|
|
|
|
case blr_varying:
|
|
|
|
case blr_cstring:
|
|
|
|
switch (newFld.dyn_dtype)
|
|
|
|
{
|
|
|
|
case blr_blob:
|
|
|
|
case blr_blob_id:
|
|
|
|
// Cannot change datatype for column %s.
|
|
|
|
// The operation cannot be performed on BLOB, or ARRAY columns.
|
|
|
|
errorCode = isc_dyn_dtype_invalid;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case blr_sql_date:
|
|
|
|
case blr_sql_time:
|
|
|
|
case blr_timestamp:
|
|
|
|
case blr_int64:
|
|
|
|
case blr_long:
|
|
|
|
case blr_short:
|
|
|
|
case blr_d_float:
|
|
|
|
case blr_double:
|
|
|
|
case blr_float:
|
|
|
|
// Cannot convert column %s from character to non-character data.
|
|
|
|
errorCode = isc_dyn_dtype_conv_invalid;
|
|
|
|
break;
|
|
|
|
|
|
|
|
// If the original field is a character field and the new field is a character field,
|
|
|
|
// is there enough space in the new field?
|
|
|
|
case blr_text:
|
|
|
|
case blr_varying:
|
|
|
|
case blr_cstring:
|
|
|
|
{
|
|
|
|
// CVC: Because our caller invoked DSC_make_descriptor() on newFld previously,
|
|
|
|
// we should have the added bytes for varchar. For cstring, we are done, since
|
|
|
|
// DSC_make_descriptor(DSC_string_length) != DSC_string_length(DSC_make_descriptor).
|
|
|
|
|
|
|
|
const USHORT maxflen = DSC_string_length(&origFld.dyn_dsc);
|
|
|
|
|
|
|
|
// We can have this assertion since this case is for both string fields.
|
|
|
|
const ULONG new_len = DSC_string_length(&newFld.dyn_dsc);
|
|
|
|
fb_assert(new_len - maxflen == (ULONG) newFld.dyn_charbytelen - origFld.dyn_charbytelen);
|
|
|
|
// if (newFld.dyn_dsc.dsc_length < maxflen)
|
|
|
|
if (new_len < maxflen)
|
|
|
|
{
|
|
|
|
// msg 208: New size specified for column %s must be at least %d characters.
|
|
|
|
errorCode = isc_dyn_char_fld_too_small;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
fb_assert(FALSE);
|
|
|
|
errorCode = 87; // MODIFY RDB$FIELDS FAILED
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// BLOB and ARRAY types
|
|
|
|
case blr_blob:
|
|
|
|
case blr_blob_id:
|
|
|
|
// Cannot change datatype for column %s.
|
|
|
|
// The operation cannot be performed on BLOB, or ARRAY columns.
|
|
|
|
errorCode = isc_dyn_dtype_invalid;
|
|
|
|
break;
|
|
|
|
|
|
|
|
// DATE types
|
|
|
|
case blr_sql_date:
|
|
|
|
case blr_sql_time:
|
|
|
|
case blr_timestamp:
|
|
|
|
switch (newFld.dyn_dtype)
|
|
|
|
{
|
|
|
|
case blr_sql_date:
|
|
|
|
if (origFld.dyn_dtype == blr_sql_time)
|
|
|
|
{
|
|
|
|
// Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported.
|
|
|
|
errorCode = isc_dyn_invalid_dtype_conversion;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case blr_sql_time:
|
|
|
|
if (origFld.dyn_dtype == blr_sql_date)
|
|
|
|
{
|
|
|
|
// Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported.
|
|
|
|
errorCode = isc_dyn_invalid_dtype_conversion;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case blr_timestamp:
|
|
|
|
if (origFld.dyn_dtype == blr_sql_time)
|
|
|
|
{
|
|
|
|
// Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported.
|
|
|
|
errorCode = isc_dyn_invalid_dtype_conversion;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// If the original field is a date field and the new field is a character field,
|
|
|
|
// is there enough space in the new field?
|
|
|
|
case blr_text:
|
|
|
|
case blr_text2:
|
|
|
|
case blr_varying:
|
|
|
|
case blr_varying2:
|
|
|
|
case blr_cstring:
|
|
|
|
case blr_cstring2:
|
|
|
|
{
|
|
|
|
const USHORT maxflen = DSC_string_length(&origFld.dyn_dsc);
|
|
|
|
|
|
|
|
// CVC: Solve bug #910423, missing DSC_string_length call.
|
|
|
|
// if (newFld.dyn_dsc.dsc_length < maxflen)
|
|
|
|
if (DSC_string_length(&newFld.dyn_dsc) < maxflen)
|
|
|
|
{
|
|
|
|
// msg 208: New size specified for column %s must be at least %d characters.
|
|
|
|
errorCode = isc_dyn_char_fld_too_small;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported.
|
|
|
|
errorCode = isc_dyn_invalid_dtype_conversion;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// NUMERIC types
|
|
|
|
case blr_int64:
|
|
|
|
case blr_long:
|
|
|
|
case blr_short:
|
|
|
|
case blr_d_float:
|
|
|
|
case blr_double:
|
|
|
|
case blr_float:
|
|
|
|
switch (newFld.dyn_dtype)
|
|
|
|
{
|
|
|
|
case blr_blob:
|
|
|
|
case blr_blob_id:
|
|
|
|
// Cannot change datatype for column %s.
|
|
|
|
// The operation cannot be performed on BLOB, or ARRAY columns.
|
|
|
|
errorCode = isc_dyn_dtype_invalid;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case blr_sql_date:
|
|
|
|
case blr_sql_time:
|
|
|
|
case blr_timestamp:
|
|
|
|
// Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported.
|
|
|
|
errorCode = isc_dyn_invalid_dtype_conversion;
|
|
|
|
break;
|
|
|
|
|
|
|
|
// If the original field is a numeric field and the new field is a numeric field,
|
|
|
|
// is there enough space in the new field (do not allow the base type to decrease)
|
|
|
|
|
|
|
|
case blr_short:
|
|
|
|
switch (origFld.dyn_dtype)
|
|
|
|
{
|
|
|
|
case blr_short:
|
|
|
|
errorCode = checkUpdateNumericType(origFld, newFld);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported.
|
|
|
|
errorCode = isc_dyn_invalid_dtype_conversion;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case blr_long:
|
|
|
|
switch (origFld.dyn_dtype)
|
|
|
|
{
|
|
|
|
case blr_long:
|
|
|
|
case blr_short:
|
|
|
|
errorCode = checkUpdateNumericType(origFld, newFld);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported.
|
|
|
|
errorCode = isc_dyn_invalid_dtype_conversion;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case blr_float:
|
|
|
|
switch (origFld.dyn_dtype)
|
|
|
|
{
|
|
|
|
case blr_float:
|
|
|
|
case blr_short:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported.
|
|
|
|
errorCode = isc_dyn_invalid_dtype_conversion;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case blr_int64:
|
|
|
|
switch (origFld.dyn_dtype)
|
|
|
|
{
|
|
|
|
case blr_int64:
|
|
|
|
case blr_long:
|
|
|
|
case blr_short:
|
|
|
|
errorCode = checkUpdateNumericType(origFld, newFld);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported.
|
|
|
|
errorCode = isc_dyn_invalid_dtype_conversion;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case blr_d_float:
|
|
|
|
case blr_double:
|
|
|
|
switch (origFld.dyn_dtype)
|
|
|
|
{
|
|
|
|
case blr_double:
|
|
|
|
case blr_d_float:
|
|
|
|
case blr_float:
|
|
|
|
case blr_short:
|
|
|
|
case blr_long:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported.
|
|
|
|
errorCode = isc_dyn_invalid_dtype_conversion;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// If the original field is a numeric field and the new field is a character field,
|
|
|
|
// is there enough space in the new field?
|
|
|
|
case blr_text:
|
|
|
|
case blr_varying:
|
|
|
|
case blr_cstring:
|
|
|
|
{
|
|
|
|
const USHORT maxflen = DSC_string_length(&origFld.dyn_dsc);
|
|
|
|
|
|
|
|
// CVC: Solve bug #910423, missing DSC_string_length call.
|
|
|
|
// if (newFld.dyn_dsc.dsc_length < maxflen)
|
|
|
|
if (DSC_string_length(&newFld.dyn_dsc) < maxflen)
|
|
|
|
{
|
|
|
|
// msg 208: New size specified for column %s must be at least %d characters.
|
|
|
|
errorCode = isc_dyn_char_fld_too_small;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
fb_assert(FALSE);
|
|
|
|
errorCode = 87; // MODIFY RDB$FIELDS FAILED
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
fb_assert(FALSE);
|
|
|
|
errorCode = 87; // MODIFY RDB$FIELDS FAILED
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (errorCode == FB_SUCCESS)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch (errorCode)
|
|
|
|
{
|
|
|
|
case isc_dyn_dtype_invalid:
|
|
|
|
// Cannot change datatype for column %s.The operation cannot be performed on DATE, BLOB, or ARRAY columns.
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(errorCode) << origFld.dyn_fld_name.c_str());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case isc_dyn_dtype_conv_invalid:
|
|
|
|
// Cannot convert column %s from character to non-character data.
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(errorCode) << origFld.dyn_fld_name.c_str());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case isc_dyn_char_fld_too_small:
|
|
|
|
// msg 208: New size specified for column %s must be at least %d characters.
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(errorCode) << origFld.dyn_fld_name.c_str() <<
|
|
|
|
Arg::Num(DSC_string_length(&origFld.dyn_dsc)));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case isc_dyn_scale_too_big:
|
|
|
|
{
|
|
|
|
int code = errorCode;
|
|
|
|
int diff = newFld.dyn_precision -
|
|
|
|
(origFld.dyn_precision + origFld.dyn_dsc.dsc_scale);
|
|
|
|
if (diff < 0)
|
|
|
|
{
|
|
|
|
// If new scale becomes negative externally, the message is useless for the user.
|
|
|
|
// (The scale is always zero or negative for us but externally is non-negative.)
|
|
|
|
// Let's ask the user to widen the precision, then. Example: numeric(4, 0) -> numeric(1, 1).
|
|
|
|
code = isc_dyn_precision_too_small;
|
|
|
|
diff = newFld.dyn_precision - newFld.dyn_dsc.dsc_scale - diff;
|
|
|
|
}
|
|
|
|
|
|
|
|
// scale_too_big: New scale specified for column @1 must be at most @2.
|
|
|
|
// precision_too_small: New precision specified for column @1 must be at least @2.
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(code) << origFld.dyn_fld_name.c_str() << Arg::Num(diff));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case isc_dyn_invalid_dtype_conversion:
|
|
|
|
{
|
|
|
|
TEXT orig_type[25], new_type[25];
|
|
|
|
|
|
|
|
DSC_get_dtype_name(&origFld.dyn_dsc, orig_type, sizeof(orig_type));
|
|
|
|
DSC_get_dtype_name(&newFld.dyn_dsc, new_type, sizeof(new_type));
|
|
|
|
|
|
|
|
// Cannot change datatype for @1. Conversion from base type @2 to @3 is not supported.
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(errorCode) << origFld.dyn_fld_name.c_str() << orig_type << new_type);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// msg 95: "MODIFY RDB$RELATION_FIELDS failed"
|
|
|
|
status_exception::raise(Arg::Gds(ENCODE_ISC_MSG(95, DYN_MSG_FAC)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compare the original field type with the new field type to determine if the original type can be
|
|
|
|
// changed to the new type.
|
|
|
|
// The types should be integral, since it tests only numeric/decimal subtypes to ensure the scale is
|
|
|
|
// not being widened at the expense of the precision, because the old stored values should fit in
|
|
|
|
// the new definition.
|
|
|
|
//
|
|
|
|
// This function returns an error code if the conversion can not be made. If the conversion can be
|
|
|
|
// made, FB_SUCCESS is returned.
|
|
|
|
ULONG AlterDomainNode::checkUpdateNumericType(const dyn_fld& origFld, const dyn_fld& newFld)
|
|
|
|
{
|
|
|
|
// Since dsc_scale is negative, the sum of precision and scale produces
|
|
|
|
// the width of the integral part.
|
|
|
|
if (origFld.dyn_sub_type && newFld.dyn_sub_type &&
|
|
|
|
origFld.dyn_precision + origFld.dyn_dsc.dsc_scale >
|
|
|
|
newFld.dyn_precision + newFld.dyn_dsc.dsc_scale)
|
|
|
|
{
|
|
|
|
return isc_dyn_scale_too_big;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FB_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Updates the field names in an index and forces the index to be rebuilt with the new field names.
|
|
|
|
void AlterDomainNode::modifyLocalFieldIndex(thread_db* tdbb, jrd_tra* transaction,
|
|
|
|
const MetaName& relationName, const MetaName& fieldName, const MetaName& newFieldName)
|
|
|
|
{
|
|
|
|
AutoRequest request;
|
|
|
|
|
|
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
|
|
IDX IN RDB$INDICES CROSS IDXS IN RDB$INDEX_SEGMENTS WITH
|
|
|
|
IDX.RDB$INDEX_NAME EQ IDXS.RDB$INDEX_NAME AND
|
|
|
|
IDX.RDB$RELATION_NAME EQ relationName.c_str() AND
|
|
|
|
IDXS.RDB$FIELD_NAME EQ fieldName.c_str()
|
|
|
|
{
|
|
|
|
// Change the name of the field in the index
|
|
|
|
MODIFY IDXS USING
|
|
|
|
memcpy(IDXS.RDB$FIELD_NAME, newFieldName.c_str(), sizeof(IDXS.RDB$FIELD_NAME));
|
|
|
|
END_MODIFY
|
|
|
|
|
|
|
|
// Set the index name to itself to tell the index to rebuild
|
|
|
|
MODIFY IDX USING
|
|
|
|
// This is to fool both gpre and gcc.
|
|
|
|
char* p = IDX.RDB$INDEX_NAME;
|
|
|
|
p[MAX_SQL_IDENTIFIER_LEN] = 0;
|
|
|
|
END_MODIFY
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
}
|
|
|
|
|
|
|
|
void AlterDomainNode::print(string& text, Array<dsql_nod*>& nodes) const
|
|
|
|
{
|
|
|
|
text.printf(
|
|
|
|
"AlterDomainNode\n"
|
|
|
|
" %s\n", name.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void AlterDomainNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
|
|
|
Attachment* attachment = transaction->tra_attachment;
|
|
|
|
|
|
|
|
// run all statements under savepoint control
|
|
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
bool endModify = false;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
AutoCacheRequest request(tdbb, drq_m_fld2, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
|
|
FLD IN RDB$FIELDS
|
|
|
|
WITH FLD.RDB$FIELD_NAME EQ name.c_str()
|
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_DOMAIN, name);
|
|
|
|
|
|
|
|
MODIFY FLD
|
|
|
|
if (dropConstraint)
|
|
|
|
{
|
|
|
|
FLD.RDB$VALIDATION_BLR.NULL = TRUE;
|
|
|
|
FLD.RDB$VALIDATION_SOURCE.NULL = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dropDefault)
|
|
|
|
{
|
|
|
|
FLD.RDB$DEFAULT_VALUE.NULL = TRUE;
|
|
|
|
FLD.RDB$DEFAULT_SOURCE.NULL = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (setConstraint)
|
|
|
|
{
|
|
|
|
if (!FLD.RDB$VALIDATION_BLR.NULL)
|
|
|
|
{
|
|
|
|
// msg 160: "Only one constraint allowed for a domain"
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(ENCODE_ISC_MSG(160, DYN_MSG_FAC)));
|
|
|
|
}
|
|
|
|
|
|
|
|
dsql_fld localField(dsqlScratch->getStatement()->getPool());
|
|
|
|
|
|
|
|
// Get the attributes of the domain, and set any occurances of
|
|
|
|
// keyword VALUE to the correct type, length, scale, etc.
|
|
|
|
if (!METD_get_domain(dsqlScratch->getTransaction(), &localField, name.c_str()))
|
|
|
|
{
|
|
|
|
// Specified domain or source field does not exist
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_sqlerr) << Arg::Num(-607) <<
|
|
|
|
Arg::Gds(isc_dsql_command_err) <<
|
|
|
|
Arg::Gds(isc_dsql_domain_not_found) << name);
|
|
|
|
}
|
|
|
|
|
|
|
|
dsqlScratch->domainValue.dsc_dtype = localField.fld_dtype;
|
|
|
|
dsqlScratch->domainValue.dsc_length = localField.fld_length;
|
|
|
|
dsqlScratch->domainValue.dsc_scale = localField.fld_scale;
|
|
|
|
|
|
|
|
dsql_str* checkString = (dsql_str*) setConstraint->nod_arg[e_cnstr_source];
|
|
|
|
string checkSource = string(checkString->str_data, checkString->str_length);
|
|
|
|
|
|
|
|
FLD.RDB$VALIDATION_SOURCE.NULL = FALSE;
|
|
|
|
attachment->storeMetaDataBlob(tdbb, transaction, &FLD.RDB$VALIDATION_SOURCE, checkSource);
|
|
|
|
|
|
|
|
dsqlScratch->getBlrData().clear();
|
|
|
|
dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5);
|
|
|
|
|
|
|
|
// Increment the context level for this statement, so that the context number for
|
|
|
|
// any RSE generated for a SELECT within the CHECK clause will be greater than 0.
|
|
|
|
// In the environment of a domain check constraint, context number 0 is reserved
|
|
|
|
// for the "blr_fid, 0, 0, 0," which is emitted for a nod_dom_value, corresponding
|
|
|
|
// to an occurance of the VALUE keyword in the body of the check constraint.
|
|
|
|
// -- chrisj 1999-08-20
|
|
|
|
++dsqlScratch->contextNumber;
|
|
|
|
|
|
|
|
dsql_nod* node = PASS1_node(dsqlScratch, setConstraint->nod_arg[e_cnstr_condition]);
|
|
|
|
|
|
|
|
GEN_hidden_variables(dsqlScratch, true);
|
|
|
|
GEN_expr(dsqlScratch, node);
|
|
|
|
|
|
|
|
dsqlScratch->appendUChar(blr_eoc);
|
|
|
|
|
|
|
|
FLD.RDB$VALIDATION_BLR.NULL = FALSE;
|
|
|
|
attachment->storeBinaryBlob(tdbb, transaction, &FLD.RDB$VALIDATION_BLR,
|
|
|
|
dsqlScratch->getBlrData());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (setDefault)
|
|
|
|
{
|
|
|
|
if (FLD.RDB$DIMENSIONS)
|
|
|
|
{
|
|
|
|
// msg 226: "Default value is not allowed for array type in domain %s"
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(ENCODE_ISC_MSG(226, DYN_MSG_FAC)) << name);
|
|
|
|
}
|
|
|
|
|
|
|
|
dsql_str* defaultString =
|
|
|
|
(dsql_str*) setDefault->nod_arg[e_dft_default_source];
|
|
|
|
string defaultSource = string(defaultString->str_data, defaultString->str_length);
|
|
|
|
|
|
|
|
FLD.RDB$DEFAULT_SOURCE.NULL = FALSE;
|
|
|
|
attachment->storeMetaDataBlob(tdbb, transaction, &FLD.RDB$DEFAULT_SOURCE, defaultSource);
|
|
|
|
|
|
|
|
dsqlScratch->getBlrData().clear();
|
|
|
|
dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5);
|
|
|
|
|
|
|
|
dsql_nod* node = PASS1_node(dsqlScratch, setDefault->nod_arg[e_dft_default]);
|
|
|
|
|
|
|
|
GEN_hidden_variables(dsqlScratch, true);
|
|
|
|
GEN_expr(dsqlScratch, node);
|
|
|
|
|
|
|
|
dsqlScratch->appendUChar(blr_eoc);
|
|
|
|
|
|
|
|
FLD.RDB$DEFAULT_VALUE.NULL = FALSE;
|
|
|
|
attachment->storeBinaryBlob(tdbb, transaction, &FLD.RDB$DEFAULT_VALUE,
|
|
|
|
dsqlScratch->getBlrData());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type)
|
|
|
|
{
|
|
|
|
type->resolve(dsqlScratch);
|
|
|
|
|
|
|
|
dyn_fld origDom, newDom;
|
|
|
|
|
|
|
|
DSC_make_descriptor(&origDom.dyn_dsc, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE,
|
|
|
|
FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE, FLD.RDB$CHARACTER_SET_ID,
|
|
|
|
FLD.RDB$COLLATION_ID);
|
|
|
|
|
|
|
|
origDom.dyn_fld_name = name;
|
|
|
|
origDom.dyn_charbytelen = FLD.RDB$FIELD_LENGTH;
|
|
|
|
origDom.dyn_dtype = FLD.RDB$FIELD_TYPE;
|
|
|
|
origDom.dyn_precision = FLD.RDB$FIELD_PRECISION;
|
|
|
|
origDom.dyn_sub_type = FLD.RDB$FIELD_SUB_TYPE;
|
|
|
|
origDom.dyn_charlen = FLD.RDB$CHARACTER_LENGTH;
|
|
|
|
origDom.dyn_collation = FLD.RDB$COLLATION_ID;
|
|
|
|
origDom.dyn_null_flag = !FLD.RDB$NULL_FLAG.NULL && FLD.RDB$NULL_FLAG != 0;
|
|
|
|
|
|
|
|
// If the original field type is an array, force its blr type to blr_blob
|
|
|
|
bool has_dimensions = false;
|
|
|
|
if (FLD.RDB$DIMENSIONS != 0)
|
|
|
|
origDom.dyn_dtype = blr_blob;
|
|
|
|
|
|
|
|
USHORT typeLength = type->length;
|
|
|
|
switch (type->type)
|
|
|
|
{
|
|
|
|
case dtype_varying:
|
|
|
|
typeLength -= sizeof(USHORT);
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Not valid for domains, but may be important for a future refactor.
|
|
|
|
case dtype_cstring:
|
|
|
|
--typeLength;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
DSC_make_descriptor(&newDom.dyn_dsc, blr_dtypes[type->type], type->scale,
|
|
|
|
typeLength, type->subType, type->charSetId, type->collationId);
|
|
|
|
|
|
|
|
newDom.dyn_fld_name = name;
|
|
|
|
newDom.dyn_charbytelen = typeLength;
|
|
|
|
newDom.dyn_dtype = blr_dtypes[type->type];
|
|
|
|
newDom.dyn_precision = type->precision;
|
|
|
|
newDom.dyn_sub_type = type->subType;
|
|
|
|
newDom.dyn_charlen = type->charLength;
|
|
|
|
newDom.dyn_collation = type->collationId;
|
|
|
|
newDom.dyn_null_flag = type->notNull;
|
|
|
|
|
|
|
|
// Now that we have all of the information needed, let's check to see if the field
|
|
|
|
// type can be modifed.
|
|
|
|
|
|
|
|
checkUpdate(origDom, newDom);
|
|
|
|
|
|
|
|
if (!newDom.dyn_dsc.isExact() || newDom.dyn_dsc.dsc_scale != 0)
|
|
|
|
{
|
|
|
|
AutoCacheRequest request(tdbb, drq_l_ident_gens, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
|
|
RFR IN RDB$RELATION_FIELDS
|
|
|
|
WITH RFR.RDB$FIELD_SOURCE = FLD.RDB$FIELD_NAME AND
|
|
|
|
RFR.RDB$GENERATOR_NAME NOT MISSING
|
|
|
|
{
|
|
|
|
// Domain @1 must be of exact number type with zero scale because it's used
|
|
|
|
// in an identity column.
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(ENCODE_ISC_MSG(276, DYN_MSG_FAC)) << name);
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the datatype was changed, update any indexes that involved the domain
|
|
|
|
|
|
|
|
AutoRequest request2;
|
|
|
|
|
|
|
|
FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction)
|
|
|
|
DOM IN RDB$RELATION_FIELDS
|
|
|
|
WITH DOM.RDB$FIELD_SOURCE EQ name.c_str()
|
|
|
|
{
|
|
|
|
modifyLocalFieldIndex(tdbb, transaction, DOM.RDB$RELATION_NAME,
|
|
|
|
DOM.RDB$FIELD_NAME, DOM.RDB$FIELD_NAME);
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
// Update RDB$FIELDS
|
|
|
|
updateRdbFields(*type,
|
|
|
|
FLD.RDB$FIELD_TYPE,
|
|
|
|
FLD.RDB$FIELD_LENGTH,
|
|
|
|
FLD.RDB$FIELD_SUB_TYPE.NULL, FLD.RDB$FIELD_SUB_TYPE,
|
|
|
|
FLD.RDB$FIELD_SCALE.NULL, FLD.RDB$FIELD_SCALE,
|
|
|
|
FLD.RDB$CHARACTER_SET_ID.NULL, FLD.RDB$CHARACTER_SET_ID,
|
|
|
|
FLD.RDB$CHARACTER_LENGTH.NULL, FLD.RDB$CHARACTER_LENGTH,
|
|
|
|
FLD.RDB$FIELD_PRECISION.NULL, FLD.RDB$FIELD_PRECISION,
|
|
|
|
FLD.RDB$COLLATION_ID.NULL, FLD.RDB$COLLATION_ID,
|
|
|
|
FLD.RDB$SEGMENT_LENGTH.NULL, FLD.RDB$SEGMENT_LENGTH);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (renameTo.hasData())
|
|
|
|
{
|
|
|
|
rename(tdbb, transaction, (FLD.RDB$DIMENSIONS.NULL ? 0 : FLD.RDB$DIMENSIONS));
|
|
|
|
strcpy(FLD.RDB$FIELD_NAME, renameTo.c_str());
|
|
|
|
}
|
|
|
|
END_MODIFY
|
|
|
|
|
|
|
|
endModify = true;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
}
|
|
|
|
catch (const status_exception& ex)
|
|
|
|
{
|
|
|
|
// msg 87: "MODIFY RDB$FIELDS failed"
|
|
|
|
rethrowMetaException(ex, ENCODE_ISC_MSG(87, DYN_MSG_FAC), endModify);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
// msg 89: "Global field not found"
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(ENCODE_ISC_MSG(89, DYN_MSG_FAC)));
|
|
|
|
}
|
|
|
|
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_DOMAIN, name);
|
|
|
|
|
|
|
|
savePoint.release(); // everything is ok
|
|
|
|
}
|
|
|
|
|
|
|
|
void AlterDomainNode::rename(thread_db* tdbb, jrd_tra* transaction, SSHORT dimensions)
|
|
|
|
{
|
|
|
|
// Checks to see if the given domain already exists.
|
|
|
|
|
|
|
|
AutoRequest request;
|
|
|
|
|
|
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
|
|
FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME EQ renameTo.c_str()
|
|
|
|
{
|
|
|
|
// msg 204: Cannot rename domain %s to %s. A domain with that name already exists.
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(ENCODE_ISC_MSG(204, DYN_MSG_FAC)) << name << renameTo);
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
// CVC: Let's update the dimensions, too.
|
|
|
|
if (dimensions != 0)
|
|
|
|
{
|
|
|
|
request.reset();
|
|
|
|
|
|
|
|
FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
|
|
FDIM IN RDB$FIELD_DIMENSIONS
|
|
|
|
WITH FDIM.RDB$FIELD_NAME EQ name.c_str()
|
|
|
|
{
|
|
|
|
MODIFY FDIM USING
|
|
|
|
strcpy(FDIM.RDB$FIELD_NAME, renameTo.c_str());
|
|
|
|
END_MODIFY
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
}
|
|
|
|
|
|
|
|
request.reset();
|
|
|
|
|
|
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
|
|
RFLD IN RDB$RELATION_FIELDS
|
|
|
|
WITH RFLD.RDB$FIELD_SOURCE EQ name.c_str()
|
|
|
|
{
|
|
|
|
MODIFY RFLD USING
|
|
|
|
strcpy(RFLD.RDB$FIELD_SOURCE, renameTo.c_str());
|
|
|
|
END_MODIFY
|
|
|
|
|
|
|
|
modifyLocalFieldIndex(tdbb, transaction, RFLD.RDB$RELATION_NAME,
|
|
|
|
RFLD.RDB$FIELD_NAME, RFLD.RDB$FIELD_NAME);
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
|
|
|
// Delete the records in RDB$FIELD_DIMENSIONS pertaining to a field.
|
|
|
|
bool DropDomainNode::deleteDimensionRecords(thread_db* tdbb, jrd_tra* transaction, const MetaName& name)
|
|
|
|
{
|
|
|
|
AutoCacheRequest request(tdbb, drq_e_dims, DYN_REQUESTS);
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
|
|
X IN RDB$FIELD_DIMENSIONS
|
|
|
|
WITH X.RDB$FIELD_NAME EQ name.c_str()
|
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
ERASE X;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
}
|
|
|
|
catch (const status_exception& ex)
|
|
|
|
{
|
|
|
|
// msg 35: "ERASE RDB$FIELDS failed"
|
|
|
|
rethrowMetaException(ex, ENCODE_ISC_MSG(35, DYN_MSG_FAC), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DropDomainNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
|
|
|
{
|
|
|
|
text.printf(
|
|
|
|
"DropDomainNode\n"
|
|
|
|
" name: '%s'\n",
|
|
|
|
name.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void DropDomainNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
|
|
|
// run all statements under savepoint control
|
|
|
|
AutoSavePoint savePoint(tdbb, transaction);
|
|
|
|
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
AutoCacheRequest request(tdbb, drq_e_gfields, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
|
|
X IN RDB$FIELDS
|
|
|
|
WITH X.RDB$FIELD_NAME EQ name.c_str()
|
|
|
|
{
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_DOMAIN, name);
|
|
|
|
|
|
|
|
check(tdbb, transaction);
|
|
|
|
deleteDimensionRecords(tdbb, transaction, name);
|
|
|
|
|
|
|
|
ERASE X;
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
}
|
|
|
|
catch (const status_exception& ex)
|
|
|
|
{
|
|
|
|
// msg 44: "ERASE RDB$FIELDS failed"
|
|
|
|
rethrowMetaException(ex, ENCODE_ISC_MSG(44, DYN_MSG_FAC), found);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found)
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_DROP_DOMAIN, name);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// msg 89: "Domain not found"
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(ENCODE_ISC_MSG(89, DYN_MSG_FAC)));
|
|
|
|
}
|
|
|
|
|
|
|
|
savePoint.release(); // everything is ok
|
|
|
|
}
|
|
|
|
|
|
|
|
void DropDomainNode::check(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
|
|
|
AutoCacheRequest request(tdbb, drq_l_fld_src, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
|
|
Y IN RDB$RELATION_FIELDS
|
|
|
|
WITH Y.RDB$FIELD_SOURCE EQ name.c_str()
|
|
|
|
{
|
|
|
|
fb_utils::exact_name_limit(Y.RDB$FIELD_SOURCE, sizeof(Y.RDB$FIELD_SOURCE));
|
|
|
|
fb_utils::exact_name_limit(Y.RDB$RELATION_NAME, sizeof(Y.RDB$RELATION_NAME));
|
|
|
|
fb_utils::exact_name_limit(Y.RDB$FIELD_NAME, sizeof(Y.RDB$FIELD_NAME));
|
|
|
|
|
|
|
|
// msg 43: "Domain %s is used in table %s (local name %s) and can not be dropped"
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(ENCODE_ISC_MSG(43, DYN_MSG_FAC)) << Y.RDB$FIELD_SOURCE <<
|
|
|
|
Y.RDB$RELATION_NAME << Y.RDB$FIELD_NAME);
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
request.reset(tdbb, drq_l_prp_src, DYN_REQUESTS);
|
|
|
|
|
|
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
|
|
X IN RDB$PROCEDURE_PARAMETERS
|
|
|
|
WITH X.RDB$FIELD_SOURCE EQ name.c_str()
|
|
|
|
{
|
|
|
|
fb_utils::exact_name_limit(X.RDB$FIELD_SOURCE, sizeof(X.RDB$FIELD_SOURCE));
|
|
|
|
fb_utils::exact_name_limit(X.RDB$PROCEDURE_NAME, sizeof(X.RDB$PROCEDURE_NAME));
|
|
|
|
fb_utils::exact_name_limit(X.RDB$PARAMETER_NAME, sizeof(X.RDB$PARAMETER_NAME));
|
|
|
|
|
|
|
|
// msg 239: "Domain %s is used in procedure %s (parameter name %s) and cannot be dropped"
|
|
|
|
status_exception::raise(
|
|
|
|
Arg::Gds(isc_no_meta_update) <<
|
|
|
|
Arg::Gds(ENCODE_ISC_MSG(239, DYN_MSG_FAC)) << X.RDB$FIELD_SOURCE <<
|
|
|
|
QualifiedName(X.RDB$PROCEDURE_NAME,
|
|
|
|
(X.RDB$PACKAGE_NAME.NULL ? NULL : X.RDB$PACKAGE_NAME)).toString().c_str() <<
|
|
|
|
X.RDB$PARAMETER_NAME);
|
|
|
|
}
|
|
|
|
END_FOR
|
|
|
|
|
|
|
|
//// FIXME: Check domain usage in functions.
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------
|
|
|
|
|
|
|
|
|
2010-01-10 18:32:40 +01:00
|
|
|
void CreateSequenceNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
|
|
|
{
|
|
|
|
text.printf(
|
|
|
|
"CreateSequenceNode\n"
|
|
|
|
" name: %s\n",
|
|
|
|
name.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CreateSequenceNode::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_SEQUENCE, name);
|
2010-01-13 20:14:15 +01:00
|
|
|
store(tdbb, transaction, name, fb_sysflag_user);
|
|
|
|
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_SEQUENCE, name);
|
|
|
|
}
|
|
|
|
|
2010-01-10 18:32:40 +01:00
|
|
|
|
2010-01-13 20:14:15 +01:00
|
|
|
void CreateSequenceNode::store(thread_db* tdbb, jrd_tra* transaction, const MetaName& name,
|
|
|
|
fb_sysflag sysFlag)
|
|
|
|
{
|
2010-01-10 18:32:40 +01:00
|
|
|
bool endStore = false;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_generator);
|
|
|
|
|
|
|
|
AutoCacheRequest request(tdbb, drq_s_gens, DYN_REQUESTS);
|
|
|
|
int faults = 0;
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
SINT64 id = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_gen_id, "RDB$GENERATORS");
|
|
|
|
|
|
|
|
id %= MAX_SSHORT + 1;
|
|
|
|
if (id == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
STORE (REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
|
|
X IN RDB$GENERATORS
|
|
|
|
{
|
|
|
|
X.RDB$GENERATOR_ID = id;
|
2010-01-13 20:14:15 +01:00
|
|
|
X.RDB$SYSTEM_FLAG = (SSHORT) sysFlag;
|
2010-01-10 18:32:40 +01:00
|
|
|
strcpy(X.RDB$GENERATOR_NAME, name.c_str());
|
|
|
|
|
|
|
|
endStore = true;
|
|
|
|
}
|
|
|
|
END_STORE
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
catch (const status_exception& ex)
|
|
|
|
{
|
|
|
|
if (ex.value()[1] != isc_no_dup)
|
|
|
|
throw;
|
|
|
|
|
|
|
|
if (++faults > MAX_SSHORT)
|
|
|
|
throw;
|
|
|
|
|
|
|
|
endStore = false;
|
|
|
|
fb_utils::init_status(tdbb->tdbb_status_vector);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (const status_exception& ex)
|
|
|
|
{
|
|
|
|
rethrowMetaException(ex, ENCODE_ISC_MSG(8, DYN_MSG_FAC), endStore); // DEFINE GENERATOR failed
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-05-19 15:47:48 +02:00
|
|
|
} // namespace Jrd
|