8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-31 10:03:03 +01:00
firebird-mirror/src/dsql/DdlNodes.epp

4229 lines
113 KiB
Plaintext
Raw Normal View History

/*
* 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"
#include "../dsql/node.h"
#include "../jrd/blr.h"
#include "../jrd/dyn.h"
#include "../jrd/flags.h"
#include "../jrd/intl.h"
#include "../jrd/jrd.h"
#include "../jrd/msg_encode.h"
#include "../jrd/obj.h"
#include "../jrd/tra.h"
2010-06-26 03:52:06 +02:00
#include "../jrd/IntlManager.h"
#include "../jrd/PreparedStatement.h"
#include "../jrd/blb_proto.h"
#include "../jrd/cmp_proto.h"
2010-07-06 02:49:33 +02:00
#include "../jrd/dsc_proto.h"
#include "../jrd/dyn_dl_proto.h"
#include "../jrd/dyn_ut_proto.h"
#include "../jrd/exe_proto.h"
#include "../jrd/intl_proto.h"
#include "../jrd/met_proto.h"
#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"
#include "../dsql/metd_proto.h"
#include "../dsql/pass1_proto.h"
#include "../common/StatusArg.h"
using namespace Firebird;
namespace Jrd {
using namespace Firebird;
using namespace Dsql;
static void rethrowMetaException(const status_exception& ex, ISC_STATUS code, bool metaDataException);
DATABASE DB = STATIC "ODS.RDB";
//----------------------
// 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 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)
{
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
DdlTriggerContext context;
2009-10-27 15:50:15 +01:00
context.ddlEvent = DDL_TRIGGER_ACTION_NAMES[action];
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)
dsqlScratch->appendUChar(blr_not_nullable);
if (type.typeOfName.hasData())
{
if (type.typeOfTable.hasData())
{
if (type.collateSpecified)
{
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);
}
else
{
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());
}
}
else
{
if (type.collateSpecified)
{
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);
}
else
{
dsqlScratch->appendUChar(blr_domain_name);
dsqlScratch->appendUChar(type.fullDomain ? blr_domain_full : blr_domain_type_of);
dsqlScratch->appendMetaString(type.typeOfName.c_str());
}
}
return;
}
switch (type.type)
{
case dtype_cstring:
case dtype_text:
case dtype_varying:
case dtype_blob:
if (!useSubType)
dsqlScratch->appendUChar(blr_dtypes[type.type]);
else if (type.type == dtype_varying)
{
dsqlScratch->appendUChar(blr_varying2);
dsqlScratch->appendUShort(type.textType);
}
else if (type.type == dtype_cstring)
{
dsqlScratch->appendUChar(blr_cstring2);
dsqlScratch->appendUShort(type.textType);
}
else if (type.type == dtype_blob)
{
dsqlScratch->appendUChar(blr_blob2);
dsqlScratch->appendUShort(type.subType);
dsqlScratch->appendUShort(type.textType);
}
else
{
dsqlScratch->appendUChar(blr_text2);
dsqlScratch->appendUShort(type.textType);
}
if (type.type == dtype_varying)
dsqlScratch->appendUShort(type.length - sizeof(USHORT));
else if (type.type != dtype_blob)
dsqlScratch->appendUShort(type.length);
break;
default:
dsqlScratch->appendUChar(blr_dtypes[type.type]);
if (DTYPE_IS_EXACT(type.type) || dtype_quad == type.type)
dsqlScratch->appendUChar(type.scale);
break;
}
}
void DdlNode::resetContextStack()
{
dsqlScratch->context->clear();
dsqlScratch->contextNumber = 0;
}
2010-07-06 02:49:33 +02:00
MetaName DdlNode::storeGlobalField(thread_db* tdbb, jrd_tra* transaction, const TypeClause& field,
MetaName& name)
{
2010-07-06 02:49:33 +02:00
bool endStore = false;
2010-07-06 02:49:33 +02:00
try
{
2010-07-06 02:49:33 +02:00
if (name.isEmpty())
DYN_UTIL_generate_field_name(tdbb, NULL, name);
2010-07-06 02:49:33 +02:00
AutoCacheRequest requestHandle(tdbb, drq_s_fld_src, DYN_REQUESTS);
2010-07-06 02:49:33 +02:00
STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
FLD IN RDB$FIELDS
{
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);
2010-07-06 02:49:33 +02:00
endStore = true;
}
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);
}
return name;
}
//----------------------
TypeClause::TypeClause(dsql_fld* aLegacyField, const MetaName& aCollate)
: legacyField(aLegacyField),
collate(aCollate)
{
}
void TypeClause::resolve(DsqlCompilerScratch* dsqlScratch)
{
DDL_resolve_intl_type(dsqlScratch, legacyField,
(collate.isEmpty() ? NULL : MAKE_cstring(collate.c_str())));
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;
collateSpecified = collate.hasData();
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());
}
//----------------------
ParameterClause::ParameterClause(dsql_fld* field, const MetaName& aCollate, dsql_nod* dflt,
dsql_nod* aLegacyParameter)
: TypeClause(field, aCollate),
name(field->fld_name),
legacyDefault(dflt),
legacyParameter(aLegacyParameter)
{
}
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
{
text.printf(
"AlterCharSetNode\n"
" charSet: %s\n"
" defaultCollation: %s\n",
charSet.c_str(), defaultCollation.c_str());
}
void AlterCharSetNode::execute(thread_db* tdbb, jrd_tra* transaction)
{
METD_drop_charset(transaction, charSet);
MET_dsql_cache_release(tdbb, SYM_intlsym_charset, charSet);
bool charSetFound = false;
bool collationFound = false;
AutoCacheRequest requestHandle1(tdbb, drq_m_charset, DYN_REQUESTS);
FOR (REQUEST_HANDLE requestHandle1 TRANSACTION_HANDLE transaction)
CS IN RDB$CHARACTER_SETS
WITH CS.RDB$CHARACTER_SET_NAME EQ charSet.c_str()
{
charSetFound = true;
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_CHARACTER_SET, charSet);
AutoCacheRequest requestHandle2(tdbb, drq_l_collation, DYN_REQUESTS);
FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction)
COLL IN RDB$COLLATIONS
WITH COLL.RDB$CHARACTER_SET_ID EQ CS.RDB$CHARACTER_SET_ID AND
COLL.RDB$COLLATION_NAME EQ defaultCollation.c_str()
{
collationFound = true;
}
2009-01-05 09:48:32 +01:00
END_FOR
if (collationFound)
{
MODIFY CS
CS.RDB$DEFAULT_COLLATE_NAME.NULL = FALSE;
strcpy(CS.RDB$DEFAULT_COLLATE_NAME, defaultCollation.c_str());
2009-01-05 09:48:32 +01:00
END_MODIFY
}
}
2009-01-05 09:48:32 +01:00
END_FOR
if (!charSetFound)
2009-12-18 00:59:07 +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
}
if (!collationFound)
{
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));
}
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_CHARACTER_SET, charSet);
}
//----------------------
2009-10-30 11:43:42 +01:00
void CommentOnNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
{
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;
const char* tableClause = NULL;
const char* columnClause = NULL;
const char* subColumnClause = NULL;
const char* addWhereClause = NULL;
Arg::StatusVector status;
switch (objType)
{
case ddl_database:
tableClause = "rdb$database";
break;
case ddl_domain:
tableClause = "rdb$fields";
columnClause = "rdb$field_name";
status << Arg::Gds(isc_dyn_domain_not_found);
break;
case ddl_relation:
if (subName.hasData())
{
tableClause = "rdb$relation_fields";
subColumnClause = "rdb$field_name";
status << Arg::Gds(isc_dyn_column_does_not_exist) <<
Arg::Str(subName) << Arg::Str(objName);
}
else
{
tableClause = "rdb$relations";
addWhereClause = "rdb$view_blr is null";
status << Arg::Gds(isc_dyn_table_not_found) << Arg::Str(objName);
}
columnClause = "rdb$relation_name";
break;
case ddl_view:
tableClause = "rdb$relations";
columnClause = "rdb$relation_name";
status << Arg::Gds(isc_dyn_view_not_found) << Arg::Str(objName);
addWhereClause = "rdb$view_blr is not null";
break;
case ddl_procedure:
if (subName.hasData())
{
tableClause = "rdb$procedure_parameters";
subColumnClause = "rdb$parameter_name";
status << Arg::Gds(isc_dyn_proc_param_not_found) <<
Arg::Str(subName) << Arg::Str(objName);
}
else
{
tableClause = "rdb$procedures";
status << Arg::Gds(isc_dyn_proc_not_found) << Arg::Str(objName);
}
addWhereClause = "rdb$package_name is null";
columnClause = "rdb$procedure_name";
break;
case ddl_trigger:
tableClause = "rdb$triggers";
columnClause = "rdb$trigger_name";
status << Arg::Gds(isc_dyn_trig_not_found) << Arg::Str(objName);
break;
case ddl_udf:
tableClause = "rdb$functions";
columnClause = "rdb$function_name";
addWhereClause = "rdb$package_name is null";
status << Arg::Gds(isc_dyn_func_not_found) << Arg::Str(objName);
break;
case ddl_blob_filter:
tableClause = "rdb$filters";
columnClause = "rdb$function_name";
status << Arg::Gds(isc_dyn_filter_not_found) << Arg::Str(objName);
break;
case ddl_exception:
tableClause = "rdb$exceptions";
columnClause = "rdb$exception_name";
status << Arg::Gds(isc_dyn_exception_not_found) << Arg::Str(objName);
break;
case ddl_generator:
tableClause = "rdb$generators";
columnClause = "rdb$generator_name";
status << Arg::Gds(isc_dyn_gen_not_found) << Arg::Str(objName);
break;
case ddl_index:
tableClause = "rdb$indices";
columnClause = "rdb$index_name";
status << Arg::Gds(isc_dyn_index_not_found) << Arg::Str(objName);
break;
case ddl_role:
tableClause = "rdb$roles";
columnClause = "rdb$role_name";
status << Arg::Gds(isc_dyn_role_not_found) << Arg::Str(objName);
break;
case ddl_charset:
tableClause = "rdb$character_sets";
columnClause = "rdb$character_set_name";
status << Arg::Gds(isc_dyn_charset_not_found) << Arg::Str(objName);
break;
case ddl_collation:
tableClause = "rdb$collations";
columnClause = "rdb$collation_name";
status << Arg::Gds(isc_dyn_collation_not_found) << Arg::Str(objName);
break;
case ddl_package:
tableClause = "rdb$packages";
columnClause = "rdb$package_name";
status << Arg::Gds(isc_dyn_package_not_found) << Arg::Str(objName);
break;
case ddl_schema:
tableClause = "rdb$schemas";
columnClause = "rdb$schema_name";
status << Arg::Gds(isc_dyn_schema_not_found) << Arg::Str(objName);
break;
default:
fb_assert(false);
return;
}
TriStateType<string> description;
if (!text.isEmpty())
description = attachment->stringToMetaCharSet(tdbb, text, textCharSet);
PreparedStatement::Builder sql;
sql << "update" << tableClause << "set rdb$description =" << description;
if (columnClause)
{
sql << "where" << columnClause << "=" << objName;
if (subColumnClause)
sql << "and" << subColumnClause << "=" << subName;
}
if (addWhereClause)
sql << "and" << addWhereClause;
2009-10-23 02:42:40 +02:00
AutoPreparedStatement ps(attachment->prepareStatement(tdbb, transaction, sql));
if (ps->executeUpdate(tdbb, transaction) == 0)
status_exception::raise(status);
}
//----------------------
2009-10-30 11:43:42 +01:00
void CreateAlterFunctionNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
{
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";
}
}
DdlNode* CreateAlterFunctionNode::internalDsqlPass()
{
DsqlCompiledStatement* const statement = dsqlScratch->getStatement();
statement->setBlockNode(this);
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) <<
Arg::Gds(isc_dsql_var_conflict) <<
Arg::Str(field->fld_name));
}
}
}
}
source.ltrim("\n\r\t ");
bool hasDefaultParams = false;
// compile default expressions
for (unsigned i = 0; i < parameters.getCount(); ++i)
{
ParameterClause& parameter = parameters[i];
if (parameter.legacyDefault)
{
hasDefaultParams = true;
parameter.legacyDefault->nod_arg[e_dft_default] =
PASS1_node(dsqlScratch, parameter.legacyDefault->nod_arg[e_dft_default]);
}
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"));
}
}
for (unsigned i = 0; i < parameters.getCount(); ++i)
parameters[i].resolve(dsqlScratch);
returnType.resolve(dsqlScratch);
return DdlNode::internalDsqlPass();
}
void CreateAlterFunctionNode::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) <<
Arg::Gds(isc_dyn_func_not_found) << Arg::Str(name));
}
}
}
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)
{
// 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);
}
}
void CreateAlterFunctionNode::executeCreate(thread_db* tdbb, jrd_tra* transaction)
{
2009-12-06 20:11:25 +01:00
Attachment* attachment = transaction->getAttachment();
if (package.isEmpty())
2009-12-24 11:32:54 +01:00
{
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_FUNCTION, name);
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
AutoCacheRequest requestHandle(tdbb, drq_s_funcs2, DYN_REQUESTS);
int faults = 0;
while (true)
{
try
{
SINT64 id = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_fun_id, "RDB$FUNCTIONS");
id %= (MAX_SSHORT + 1);
if (!id)
continue;
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());
FUN.RDB$LEGACY_FLAG.NULL = FALSE;
FUN.RDB$LEGACY_FLAG = 0;
FUN.RDB$INVARIANT_FLAG.NULL = FALSE;
FUN.RDB$INVARIANT_FLAG = invariant ? TRUE : FALSE;
FUN.RDB$RETURN_ARGUMENT.NULL = FALSE;
FUN.RDB$RETURN_ARGUMENT = 0;
if (package.hasData())
{
FUN.RDB$PACKAGE_NAME.NULL = FALSE;
strcpy(FUN.RDB$PACKAGE_NAME, package.c_str());
FUN.RDB$PRIVATE_FLAG.NULL = FALSE;
FUN.RDB$PRIVATE_FLAG = privateScope;
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
break;
}
2010-01-10 18:35:11 +01:00
catch (const status_exception& ex)
{
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_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
}
}
executeAlter(tdbb, transaction, false, false);
}
bool CreateAlterFunctionNode::executeAlter(thread_db* tdbb, jrd_tra* transaction,
bool secondPass, bool runTriggers)
{
2009-12-06 20:11:25 +01:00
Attachment* attachment = transaction->getAttachment();
bool modified = false;
AutoCacheRequest requestHandle(tdbb, drq_m_funcs2, DYN_REQUESTS);
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
FUN IN RDB$FUNCTIONS
WITH FUN.RDB$FUNCTION_NAME EQ name.c_str() AND
FUN.RDB$PACKAGE_NAME EQUIV NULLIF(package.c_str(), '')
{
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);
}
if (!secondPass && runTriggers && package.isEmpty())
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_FUNCTION, name);
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());
FUN.RDB$DEBUG_INFO.NULL = FALSE;
attachment->storeBinaryBlob(tdbb, transaction, &FUN.RDB$DEBUG_INFO,
2010-07-06 02:49:33 +02:00
dsqlScratch->getDebugData());
}
}
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
ParameterClause returnParameter(returnType.legacyField, returnType.collate, NULL, NULL);
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());
// Avoid the return with name INTERNAL_FIELD_NAME.
if (parameter.name.hasData() && pos != 0)
{
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;
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;
ARG.RDB$ARGUMENT_MECHANISM.NULL = FALSE;
ARG.RDB$ARGUMENT_MECHANISM =
(USHORT) (parameter.fullDomain ? prm_mech_normal : prm_mech_type_of);
if (parameter.notNull)
{
ARG.RDB$NULL_FLAG.NULL = FALSE;
ARG.RDB$NULL_FLAG = TRUE;
}
ARG.RDB$FIELD_SOURCE.NULL = FALSE;
if (parameter.typeOfTable.isEmpty())
{
if (parameter.typeOfName.hasData())
strcpy(ARG.RDB$FIELD_SOURCE, parameter.typeOfName.c_str());
else
{
2010-07-06 02:49:33 +02:00
MetaName fieldName;
storeGlobalField(tdbb, transaction, parameter, fieldName);
strcpy(ARG.RDB$FIELD_SOURCE, fieldName.c_str());
}
}
else
{
ARG.RDB$RELATION_NAME.NULL = FALSE;
strcpy(ARG.RDB$RELATION_NAME, parameter.typeOfTable.c_str());
ARG.RDB$FIELD_NAME.NULL = FALSE;
strcpy(ARG.RDB$FIELD_NAME, parameter.typeOfName.c_str());
strcpy(ARG.RDB$FIELD_SOURCE, parameter.fieldSource.c_str());
}
// 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.
if (parameter.collateSpecified && parameter.typeOfName.hasData())
{
ARG.RDB$COLLATION_ID.NULL = FALSE;
ARG.RDB$COLLATION_ID = parameter.collationId;
}
// 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.
if (parameter.legacyDefault)
{
ARG.RDB$DEFAULT_VALUE.NULL = FALSE;
ARG.RDB$DEFAULT_SOURCE.NULL = FALSE;
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);
dsqlScratch->getBlrData().clear();
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
dsqlScratch->appendUChar(blr_version4);
else
dsqlScratch->appendUChar(blr_version5);
GEN_expr(dsqlScratch, parameter.legacyDefault->nod_arg[e_dft_default]);
dsqlScratch->appendUChar(blr_eoc);
attachment->storeBinaryBlob(tdbb, transaction, &ARG.RDB$DEFAULT_VALUE,
2010-07-06 02:49:33 +02:00
dsqlScratch->getBlrData());
}
}
END_STORE
}
2009-12-24 11:42:32 +01:00
void CreateAlterFunctionNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
{
if (invalid)
2010-01-02 10:42:09 +01:00
status_exception::raise(Arg::Gds(isc_dyn_invalid_ddl_func) << name);
if (compiled)
return;
compiled = true;
invalid = true;
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
if (body)
{
dsqlScratch->beginDebug();
dsqlScratch->getBlrData().clear();
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
dsqlScratch->appendUChar(blr_version4);
else
dsqlScratch->appendUChar(blr_version5);
dsqlScratch->appendUChar(blr_begin);
if (parameters.getCount() != 0)
{
2009-12-24 11:42:32 +01:00
fb_assert(parameters.getCount() < size_t(MAX_USHORT / 2));
dsqlScratch->appendUChar(blr_message);
dsqlScratch->appendUChar(0);
dsqlScratch->appendUShort(2 * parameters.getCount());
for (unsigned i = 0; i < parameters.getCount(); ++i)
{
ParameterClause& parameter = parameters[i];
dsqlScratch->putDebugArgument(fb_dbg_arg_input, i,
parameter.name.c_str());
putType(parameter, true);
// add slot for null flag (parameter2)
dsqlScratch->appendUChar(blr_short);
dsqlScratch->appendUChar(0);
variables.add(MAKE_variable(parameter.legacyField,
parameter.name.c_str(), VAR_input, 0, (USHORT) (2 * i), 0));
}
}
dsqlScratch->appendUChar(blr_message);
dsqlScratch->appendUChar(1);
dsqlScratch->appendUShort(2);
dsqlScratch->putDebugArgument(fb_dbg_arg_output, 0, "");
putType(returnType, true);
// add slot for null flag (parameter2)
dsqlScratch->appendUChar(blr_short);
dsqlScratch->appendUChar(0);
dsql_nod* const var = MAKE_variable(returnType.legacyField, "", VAR_output, 1, 0, 0);
variables.add(var);
outputVariables.add(var);
if (parameters.getCount() != 0)
{
dsqlScratch->appendUChar(blr_receive);
dsqlScratch->appendUChar(0);
}
dsqlScratch->appendUChar(blr_begin);
for (unsigned i = 0; i < parameters.getCount(); ++i)
{
ParameterClause& parameter = parameters[i];
if (parameter.fullDomain || parameter.notNull)
{
// ASF: To validate input parameters we need only to read its value.
// Assigning it to null is an easy way to do this.
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);
}
}
dsql_var* const variable = (dsql_var*) outputVariables[0]->nod_arg[Dsql::e_var_variable];
putLocalVariable(dsqlScratch, variable, 0, NULL);
// ASF: This is here to not change the old logic (proc_flag)
// of previous calls to PASS1_node and PASS1_statement.
dsqlScratch->setPsql(true);
putLocalVariables(dsqlScratch, localDeclList, 1);
dsqlScratch->appendUChar(blr_stall);
// put a label before body of procedure,
// so that any EXIT statement can get out
dsqlScratch->appendUChar(blr_label);
dsqlScratch->appendUChar(0);
dsqlScratch->loopLevel = 0;
dsqlScratch->cursorNumber = 0;
GEN_statement(dsqlScratch, PASS1_statement(dsqlScratch, body));
statement->setType(DsqlCompiledStatement::TYPE_DDL);
dsqlScratch->appendUChar(blr_end);
genReturn(dsqlScratch, false);
dsqlScratch->appendUChar(blr_end);
dsqlScratch->appendUChar(blr_eoc);
dsqlScratch->endDebug();
}
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)
{
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)
{
AutoCacheRequest requestHandle2(tdbb, drq_e_arg_gfld, DYN_REQUESTS);
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
{
bool erase = true;
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;
}
END_FOR
}
ERASE ARG;
}
END_FOR
}
2009-10-30 11:43:42 +01:00
void DropFunctionNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
{
text.printf(
"DropFunctionNode\n"
" name: '%s'\n",
name.c_str());
}
DdlNode* DropFunctionNode::internalDsqlPass()
{
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_FUNCTION);
return DdlNode::internalDsqlPass();
}
void DropFunctionNode::execute(thread_db* tdbb, jrd_tra* transaction)
{
// run all statements under savepoint control
AutoSavePoint savePoint(tdbb, transaction);
bool found = false;
dropArguments(tdbb, transaction, name, package);
AutoCacheRequest requestHandle(tdbb, drq_e_funcs, DYN_REQUESTS);
2009-10-23 02:42:40 +02:00
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
FUN IN RDB$FUNCTIONS
WITH FUN.RDB$FUNCTION_NAME EQ name.c_str() AND
FUN.RDB$PACKAGE_NAME EQUIV NULLIF(package.c_str(), '')
{
if (!FUN.RDB$SYSTEM_FLAG.NULL && FUN.RDB$SYSTEM_FLAG)
{
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_dyn_cannot_mod_sysfunc) << FUN.RDB$FUNCTION_NAME);
}
if (package.isEmpty())
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_FUNCTION, name);
ERASE FUN;
if (!FUN.RDB$SECURITY_CLASS.NULL)
DYN_delete_security_class2(transaction, FUN.RDB$SECURITY_CLASS);
found = true;
}
END_FOR
if (!found && !silent)
{
status_exception::raise(
Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_dyn_func_not_found) << Arg::Str(name));
}
if (package.isEmpty())
{
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
}
if (found && package.isEmpty())
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_DROP_FUNCTION, name);
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-12-24 11:42:32 +01:00
void RecreateFunctionNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
{
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);
dropNode.executeDdl(tdbb, transaction);
createNode->executeDdl(tdbb, transaction);
savePoint.release(); // everything is ok
}
//----------------------
2009-10-30 11:43:42 +01:00
void CreateAlterProcedureNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
{
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";
}
}
DdlNode* CreateAlterProcedureNode::internalDsqlPass()
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
statement->setBlockNode(this);
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_PROCEDURE);
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);
}
for (size_t i = 0; i < returns.getCount(); ++i)
{
ParameterClause& parameter = returns[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) <<
Arg::Gds(isc_dsql_var_conflict) << Arg::Str(field->fld_name));
}
}
}
}
source.ltrim("\n\r\t ");
bool hasDefaultParams = false;
// compile default expressions
for (unsigned i = 0; i < parameters.getCount(); ++i)
{
ParameterClause& parameter = parameters[i];
if (parameter.legacyDefault)
{
hasDefaultParams = true;
parameter.legacyDefault->nod_arg[e_dft_default] =
PASS1_node(dsqlScratch, parameter.legacyDefault->nod_arg[e_dft_default]);
}
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"));
}
}
for (unsigned i = 0; i < parameters.getCount(); ++i)
parameters[i].resolve(dsqlScratch);
for (unsigned i = 0; i < returns.getCount(); ++i)
returns[i].resolve(dsqlScratch);
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) <<
Arg::Gds(isc_dyn_proc_not_found) << Arg::Str(name));
}
}
}
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_PROCEDURE : DDL_TRIGGER_CREATE_PROCEDURE), name);
}
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);
}
}
void CreateAlterProcedureNode::executeCreate(thread_db* tdbb, jrd_tra* transaction)
{
2009-12-06 20:11:25 +01:00
Attachment* attachment = transaction->getAttachment();
if (package.isEmpty())
{
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_PROCEDURE, name);
DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_procedure);
}
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;
strcpy(P.RDB$PROCEDURE_NAME, name.c_str());
if (package.hasData())
{
P.RDB$PACKAGE_NAME.NULL = FALSE;
strcpy(P.RDB$PACKAGE_NAME, package.c_str());
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)
{
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
{
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_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();
AutoCacheRequest requestHandle(tdbb, drq_m_prcs2, DYN_REQUESTS);
bool modified = false;
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
P IN RDB$PROCEDURES
WITH P.RDB$PROCEDURE_NAME EQ name.c_str() AND
P.RDB$PACKAGE_NAME EQUIV NULLIF(package.c_str(), '')
{
if (!P.RDB$SYSTEM_FLAG.NULL && P.RDB$SYSTEM_FLAG)
{
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_dyn_cannot_mod_sysproc) << P.RDB$PROCEDURE_NAME);
}
if (!secondPass && runTriggers && package.isEmpty())
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_PROCEDURE, name);
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;
P.RDB$VALID_BLR = TRUE;
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;
}
if (external)
{
if (secondPass)
{
P.RDB$PROCEDURE_TYPE.NULL = FALSE;
P.RDB$PROCEDURE_TYPE = (USHORT) prc_selectable;
}
else
{
P.RDB$ENGINE_NAME.NULL = FALSE;
strcpy(P.RDB$ENGINE_NAME, external->engine.c_str());
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;
attachment->storeBinaryBlob(tdbb, transaction, &P.RDB$PROCEDURE_BLR,
2010-07-06 02:49:33 +02:00
dsqlScratch->getBlrData());
P.RDB$DEBUG_INFO.NULL = FALSE;
attachment->storeBinaryBlob(tdbb, transaction, &P.RDB$DEBUG_INFO,
2010-07-06 02:49:33 +02:00
dsqlScratch->getDebugData());
P.RDB$PROCEDURE_TYPE.NULL = FALSE;
P.RDB$PROCEDURE_TYPE = (USHORT)
(statement->getFlags() & DsqlCompiledStatement::FLAG_SELECTABLE ?
prc_selectable : prc_executable);
}
}
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);
}
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
{
MODIFY RFR
{
strcpy(RFR.RDB$FIELD_SOURCE, PRM.RDB$FIELD_SOURCE);
}
END_MODIFY
}
END_FOR
}
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();
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;
strcpy(PRM.RDB$PARAMETER_NAME, parameter.name.c_str());
PRM.RDB$PROCEDURE_NAME.NULL = FALSE;
strcpy(PRM.RDB$PROCEDURE_NAME, name.c_str());
if (package.hasData())
{
PRM.RDB$PACKAGE_NAME.NULL = FALSE;
strcpy(PRM.RDB$PACKAGE_NAME, package.c_str());
}
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())
strcpy(PRM.RDB$FIELD_SOURCE, parameter.typeOfName.c_str());
else
{
2010-07-06 02:49:33 +02:00
MetaName fieldName;
storeGlobalField(tdbb, transaction, parameter, fieldName);
strcpy(PRM.RDB$FIELD_SOURCE, fieldName.c_str());
}
}
else
{
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());
}
// 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;
// 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.
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);
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
dsqlScratch->getBlrData().clear();
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
dsqlScratch->appendUChar(blr_version4);
else
dsqlScratch->appendUChar(blr_version5);
GEN_expr(dsqlScratch, parameter.legacyDefault->nod_arg[e_dft_default]);
dsqlScratch->appendUChar(blr_eoc);
attachment->storeBinaryBlob(tdbb, transaction, &PRM.RDB$DEFAULT_VALUE,
2010-07-06 02:49:33 +02:00
dsqlScratch->getBlrData());
}
}
END_STORE
}
2009-10-30 11:43:42 +01:00
void CreateAlterProcedureNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
{
if (invalid)
status_exception::raise(Arg::Gds(isc_dyn_invalid_ddl_proc) << name);
if (compiled)
return;
compiled = true;
2009-10-24 20:38:25 +02:00
if (!body)
return;
invalid = true;
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
dsqlScratch->beginDebug();
dsqlScratch->getBlrData().clear();
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
dsqlScratch->appendUChar(blr_version4);
2009-10-24 20:38:25 +02:00
else
dsqlScratch->appendUChar(blr_version5);
dsqlScratch->appendUChar(blr_begin);
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);
dsqlScratch->appendUChar(blr_message);
dsqlScratch->appendUChar(0);
dsqlScratch->appendUShort(2 * parameters.getCount());
2009-10-24 20:38:25 +02:00
for (unsigned i = 0; i < parameters.getCount(); ++i)
{
ParameterClause& parameter = parameters[i];
dsqlScratch->putDebugArgument(fb_dbg_arg_input, i, parameter.name.c_str());
2009-10-24 20:38:25 +02:00
putType(parameter, true);
2009-10-24 20:38:25 +02:00
// add slot for null flag (parameter2)
dsqlScratch->appendUChar(blr_short);
dsqlScratch->appendUChar(0);
variables.add(MAKE_variable(parameter.legacyField,
parameter.name.c_str(), VAR_input, 0, (USHORT) (2 * i), 0));
}
2009-10-24 20:38:25 +02:00
}
2009-10-30 11:43:42 +01:00
fb_assert(returns.getCount() < MAX_USHORT / 2);
dsqlScratch->appendUChar(blr_message);
dsqlScratch->appendUChar(1);
dsqlScratch->appendUShort(2 * returns.getCount() + 1);
2009-10-24 20:38:25 +02:00
if (returns.getCount() != 0)
{
for (unsigned i = 0; i < returns.getCount(); ++i)
{
2009-10-24 20:38:25 +02:00
ParameterClause& parameter = returns[i];
dsqlScratch->putDebugArgument(fb_dbg_arg_output, i, parameter.name.c_str());
2009-10-24 20:38:25 +02:00
putType(parameter, true);
2009-10-24 20:38:25 +02:00
// add slot for null flag (parameter2)
dsqlScratch->appendUChar(blr_short);
dsqlScratch->appendUChar(0);
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);
variables.add(var);
outputVariables.add(var);
}
2009-10-24 20:38:25 +02:00
}
// add slot for EOS
dsqlScratch->appendUChar(blr_short);
dsqlScratch->appendUChar(0);
2009-10-24 20:38:25 +02:00
if (parameters.getCount() != 0)
{
dsqlScratch->appendUChar(blr_receive);
dsqlScratch->appendUChar(0);
2009-10-24 20:38:25 +02:00
}
dsqlScratch->appendUChar(blr_begin);
2009-10-24 20:38:25 +02:00
for (unsigned i = 0; i < parameters.getCount(); ++i)
{
ParameterClause& parameter = parameters[i];
2009-10-24 20:38:25 +02:00
if (parameter.fullDomain || parameter.notNull)
{
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.
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-24 20:38:25 +02:00
}
for (Array<dsql_nod*>::const_iterator i = outputVariables.begin(); i != outputVariables.end(); ++i)
2009-10-24 20:38:25 +02:00
{
dsql_nod* parameter = *i;
dsql_var* const variable = (dsql_var*) parameter->nod_arg[Dsql::e_var_variable];
putLocalVariable(dsqlScratch, variable, 0, NULL);
2009-10-24 20:38:25 +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.
dsqlScratch->setPsql(true);
putLocalVariables(dsqlScratch, localDeclList, returns.getCount());
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
dsqlScratch->appendUChar(blr_label);
dsqlScratch->appendUChar(0);
dsqlScratch->loopLevel = 0;
dsqlScratch->cursorNumber = 0;
GEN_statement(dsqlScratch, PASS1_statement(dsqlScratch, body));
statement->setType(DsqlCompiledStatement::TYPE_DDL);
dsqlScratch->appendUChar(blr_end);
genReturn(dsqlScratch, true);
dsqlScratch->appendUChar(blr_end);
dsqlScratch->appendUChar(blr_eoc);
dsqlScratch->endDebug();
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)
{
AutoCacheRequest requestHandle(tdbb, drq_e_prms2, DYN_REQUESTS);
2009-10-23 02:42:40 +02:00
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
PRM IN RDB$PROCEDURE_PARAMETERS
WITH PRM.RDB$PROCEDURE_NAME EQ procedureName.c_str() AND
PRM.RDB$PACKAGE_NAME EQUIV NULLIF(packageName.c_str(), '')
{
// get rid of parameters in rdb$fields
if (!PRM.RDB$FIELD_SOURCE.NULL && PRM.RDB$RELATION_NAME.NULL && PRM.RDB$FIELD_NAME.NULL)
{
AutoCacheRequest requestHandle2(tdbb, drq_e_prm_gfld, DYN_REQUESTS);
FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction)
FLD IN RDB$FIELDS
WITH FLD.RDB$FIELD_NAME EQ PRM.RDB$FIELD_SOURCE AND
FLD.RDB$FIELD_NAME STARTING WITH IMPLICIT_DOMAIN_PREFIX AND
(FLD.RDB$SYSTEM_FLAG EQ 0 OR FLD.RDB$SYSTEM_FLAG MISSING)
{
ERASE FLD;
}
END_FOR
}
ERASE PRM;
}
END_FOR
}
2009-10-30 11:43:42 +01:00
void DropProcedureNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
{
text.printf(
"DropProcedureNode\n"
" name: '%s'\n",
name.c_str());
}
DdlNode* DropProcedureNode::internalDsqlPass()
{
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_PROCEDURE);
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;
dropParameters(tdbb, transaction, name, package);
AutoCacheRequest requestHandle(tdbb, drq_e_prcs2, DYN_REQUESTS);
2009-10-23 02:42:40 +02:00
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
PRC IN RDB$PROCEDURES
WITH PRC.RDB$PROCEDURE_NAME EQ name.c_str() AND
PRC.RDB$PACKAGE_NAME EQUIV NULLIF(package.c_str(), '')
{
if (!PRC.RDB$SYSTEM_FLAG.NULL && PRC.RDB$SYSTEM_FLAG)
{
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_dyn_cannot_mod_sysproc) << PRC.RDB$PROCEDURE_NAME);
}
if (package.isEmpty())
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_PROCEDURE, name);
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) <<
Arg::Gds(isc_dyn_proc_not_found) << Arg::Str(name));
}
if (package.isEmpty())
{
requestHandle.reset(tdbb, drq_e_prc_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_procedure
{
ERASE PRIV;
}
END_FOR
requestHandle.reset(tdbb, drq_e_prc_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_procedure
{
ERASE PRIV;
}
END_FOR
}
if (found && package.isEmpty())
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PROCEDURE, name);
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-30 11:43:42 +01:00
void RecreateProcedureNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
{
text.printf("RecreateProcedureNode\n");
}
DdlNode* RecreateProcedureNode::internalDsqlPass()
{
dropNode.dsqlPass(dsqlScratch);
createNode->dsqlPass(dsqlScratch);
return DdlNode::internalDsqlPass();
}
void RecreateProcedureNode::execute(thread_db* tdbb, jrd_tra* transaction)
{
// run all statements under savepoint control
AutoSavePoint savePoint(tdbb, transaction);
dropNode.executeDdl(tdbb, transaction);
createNode->executeDdl(tdbb, transaction);
savePoint.release(); // everything is ok
}
//----------------------
2009-10-30 11:43:42 +01:00
void CreateAlterTriggerNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
{
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;
}
}
DdlNode* CreateAlterTriggerNode::internalDsqlPass()
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
statement->setBlockNode(this);
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_TRIGGER);
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();
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)
{
AutoRequest requestHandle;
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
TRG IN RDB$TRIGGERS
WITH TRG.RDB$TRIGGER_NAME EQ name.c_str()
{
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) <<
Arg::Gds(isc_dyn_trig_not_found) << Arg::Str(name));
}
}
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) <<
Arg::Gds(isc_dyn_trig_not_found) << Arg::Str(name));
}
}
}
else
executeCreate(tdbb, transaction);
savePoint.release(); // everything is ok
}
void CreateAlterTriggerNode::executeCreate(thread_db* tdbb, jrd_tra* transaction)
{
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_TRIGGER, name);
AutoCacheRequest requestHandle(tdbb, drq_s_triggers2, DYN_REQUESTS);
bool endStore = false;
try
{
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());
fb_assert(type.specified);
TRG.RDB$TRIGGER_TYPE = type.value;
TRG.RDB$TRIGGER_SEQUENCE = (!position.specified ? 0 : position.value);
TRG.RDB$TRIGGER_INACTIVE = (!active.specified ? 0 : (USHORT) !active.value);
endStore = true;
}
END_STORE
}
catch (const status_exception& ex)
{
rethrowMetaException(ex, ENCODE_ISC_MSG(31, DYN_MSG_FAC), endStore); // DEFINE TRIGGER failed
}
executeAlter(tdbb, transaction, false);
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_TRIGGER, name);
}
bool CreateAlterTriggerNode::executeAlter(thread_db* tdbb, jrd_tra* transaction, bool runTriggers)
{
2009-12-06 20:11:25 +01:00
Attachment* attachment = transaction->getAttachment();
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);
try
{
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
TRG IN RDB$TRIGGERS
WITH TRG.RDB$TRIGGER_NAME EQ name.c_str()
{
if (type.specified && type.value != (FB_UINT64) TRG.RDB$TRIGGER_TYPE &&
((create && relationName.isEmpty()) || TRG.RDB$RELATION_NAME.NULL))
{
status_exception::raise(
Arg::Gds(isc_dsql_command_err) <<
Arg::Gds(isc_dsql_db_trigger_type_cant_change));
}
if (!TRG.RDB$SYSTEM_FLAG.NULL)
{
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;
}
}
if (runTriggers)
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_TRIGGER, name);
MODIFY TRG
if (body || external)
{
fb_assert(!(body && external));
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;
if (position.specified)
TRG.RDB$TRIGGER_SEQUENCE = position.value;
if (active.specified)
TRG.RDB$TRIGGER_INACTIVE = (USHORT) !active.value;
if (external)
{
TRG.RDB$ENGINE_NAME.NULL = FALSE;
strcpy(TRG.RDB$ENGINE_NAME, external->engine.c_str());
if (external->name.length() >= sizeof(TRG.RDB$ENTRYPOINT))
{
status_exception::raise(
Arg::Gds(isc_arith_except) <<
Arg::Gds(isc_string_truncation));
}
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());
TRG.RDB$DEBUG_INFO.NULL = FALSE;
attachment->storeBinaryBlob(tdbb, transaction, &TRG.RDB$DEBUG_INFO,
2010-07-06 02:49:33 +02:00
dsqlScratch->getDebugData());
}
if (source.hasData())
{
TRG.RDB$TRIGGER_SOURCE.NULL = FALSE;
attachment->storeMetaDataBlob(tdbb, transaction, &TRG.RDB$TRIGGER_SOURCE, source);
}
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
}
if (modified && runTriggers)
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_TRIGGER, name);
return modified;
}
2009-10-30 11:43:42 +01:00
void CreateAlterTriggerNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/)
{
if (invalid)
status_exception::raise(Arg::Gds(isc_dyn_invalid_ddl_trig) << name);
if (compiled)
return;
compiled = true;
invalid = true;
if (body)
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
dsqlScratch->beginDebug();
dsqlScratch->getBlrData().clear();
// 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.
if (dsqlScratch->contextNumber)
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);
dsql_ctx* oldContext = PASS1_make_context(dsqlScratch, relationNode);
oldContext->ctx_flags |= CTX_system;
}
else
dsqlScratch->contextNumber++;
if (hasNewContext(type.value))
{
relationNode->nod_arg[e_rln_alias] = (dsql_nod*) MAKE_cstring(NEW_CONTEXT);
dsql_ctx* newContext = PASS1_make_context(dsqlScratch, relationNode);
newContext->ctx_flags |= CTX_system;
}
else
dsqlScratch->contextNumber++;
relationNode->nod_arg[e_rln_alias] = temp;
}
// generate the trigger blr
if (statement->getFlags() & DsqlCompiledStatement::FLAG_BLR_VERSION4)
dsqlScratch->appendUChar(blr_version4);
else
dsqlScratch->appendUChar(blr_version5);
dsqlScratch->appendUChar(blr_begin);
dsqlScratch->setPsql(true);
putLocalVariables(dsqlScratch, localDeclList, 0);
dsqlScratch->scopeLevel++;
// 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.
dsqlScratch->appendUChar(blr_label);
dsqlScratch->appendUChar(0);
dsqlScratch->loopLevel = 0;
dsqlScratch->cursorNumber = 0;
GEN_statement(dsqlScratch, PASS1_statement(dsqlScratch, body));
dsqlScratch->scopeLevel--;
dsqlScratch->appendUChar(blr_end);
dsqlScratch->appendUChar(blr_eoc);
dsqlScratch->endDebug();
// 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.
statement->setType(DsqlCompiledStatement::TYPE_DDL);
}
invalid = false;
}
//----------------------
2009-10-30 11:43:42 +01:00
void DropTriggerNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
{
text.printf(
"DropTriggerNode\n"
" name: '%s'\n",
name.c_str());
}
DdlNode* DropTriggerNode::internalDsqlPass()
{
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_TRIGGER);
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
WITH X.RDB$TRIGGER_NAME EQ name.c_str()
{
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-12-06 20:11:25 +01:00
if (X.RDB$RELATION_NAME.NULL && !transaction->getAttachment()->locksmith())
status_exception::raise(Arg::Gds(isc_adm_task_denied));
executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_TRIGGER, name);
relationName = X.RDB$RELATION_NAME;
ERASE X;
found = true;
}
END_FOR
if (!found && !silent)
{
status_exception::raise(
Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_dyn_trig_not_found) << Arg::Str(name));
}
requestHandle.reset(tdbb, drq_e_trg_msgs3, DYN_REQUESTS);
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
TM IN RDB$TRIGGER_MESSAGES
WITH TM.RDB$TRIGGER_NAME EQ name.c_str()
{
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
WITH PRIV.RDB$USER EQ name.c_str() AND
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)
executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_DROP_TRIGGER, name);
savePoint.release(); // everything is ok
}
//----------------------
2009-10-30 11:43:42 +01:00
void RecreateTriggerNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
{
text.printf("RecreateTriggerNode\n");
}
DdlNode* RecreateTriggerNode::internalDsqlPass()
{
dropNode.dsqlPass(dsqlScratch);
createNode->dsqlPass(dsqlScratch);
return DdlNode::internalDsqlPass();
}
void RecreateTriggerNode::execute(thread_db* tdbb, jrd_tra* transaction)
{
// run all statements under savepoint control
AutoSavePoint savePoint(tdbb, transaction);
dropNode.executeDdl(tdbb, transaction);
createNode->executeDdl(tdbb, transaction);
savePoint.release(); // everything is ok
}
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);
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
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;
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
}
}
} // namespace Jrd