From: <asf...@us...> - 2010-07-26 02:38:13
|
Revision: 51368 http://firebird.svn.sourceforge.net/firebird/?rev=51368&view=rev Author: asfernandes Date: 2010-07-26 02:37:57 +0000 (Mon, 26 Jul 2010) Log Message: ----------- Refactor CREATE/ALTER/DROP/RECREATE TABLE and DROP VIEW commands Modified Paths: -------------- firebird/trunk/lang_helpers/gds_codes.ftn firebird/trunk/lang_helpers/gds_codes.pas firebird/trunk/src/common/classes/ByteChunk.h firebird/trunk/src/common/classes/objects_array.h firebird/trunk/src/dsql/BlrWriter.h firebird/trunk/src/dsql/DdlNodes.epp firebird/trunk/src/dsql/DdlNodes.h firebird/trunk/src/dsql/Nodes.h firebird/trunk/src/dsql/PackageNodes.epp firebird/trunk/src/dsql/PackageNodes.h firebird/trunk/src/dsql/ddl.cpp firebird/trunk/src/dsql/ddl_proto.h firebird/trunk/src/dsql/dsql.h firebird/trunk/src/dsql/node.h firebird/trunk/src/dsql/parse.y firebird/trunk/src/dsql/pass1.cpp firebird/trunk/src/gpre/gpre.h firebird/trunk/src/include/gen/codetext.h firebird/trunk/src/include/gen/iberror.h firebird/trunk/src/include/gen/msgs.h firebird/trunk/src/include/gen/sql_code.h firebird/trunk/src/include/gen/sql_state.h firebird/trunk/src/jrd/constants.h firebird/trunk/src/jrd/drq.h firebird/trunk/src/jrd/dyn.epp firebird/trunk/src/jrd/dyn_def.epp firebird/trunk/src/jrd/dyn_del.epp firebird/trunk/src/jrd/dyn_df_proto.h firebird/trunk/src/jrd/dyn_dl_proto.h firebird/trunk/src/jrd/dyn_md_proto.h firebird/trunk/src/jrd/dyn_mod.epp firebird/trunk/src/jrd/dyn_ut_proto.h firebird/trunk/src/jrd/dyn_util.epp firebird/trunk/src/msgs/facilities2.sql firebird/trunk/src/msgs/messages2.sql firebird/trunk/src/msgs/system_errors2.sql Modified: firebird/trunk/lang_helpers/gds_codes.ftn =================================================================== --- firebird/trunk/lang_helpers/gds_codes.ftn 2010-07-26 02:14:48 UTC (rev 51367) +++ firebird/trunk/lang_helpers/gds_codes.ftn 2010-07-26 02:37:57 UTC (rev 51368) @@ -1996,6 +1996,86 @@ PARAMETER (GDS__dsql_invalid_type_neg = 336397256) INTEGER*4 GDS__dsql_max_distinct_items PARAMETER (GDS__dsql_max_distinct_items = 336397257) + INTEGER*4 GDS__dsql_alter_charset_failed + PARAMETER (GDS__dsql_alter_charset_failed = 336397258) + INTEGER*4 GDS__dsql_comment_on_failed + PARAMETER (GDS__dsql_comment_on_failed = 336397259) + INTEGER*4 GDS__dsql_create_func_failed + PARAMETER (GDS__dsql_create_func_failed = 336397260) + INTEGER*4 GDS__dsql_alter_func_failed + PARAMETER (GDS__dsql_alter_func_failed = 336397261) + INTEGER*4 GDS__dsql_create_alter_func_failed + PARAMETER (GDS__dsql_create_alter_func_failed = 336397262) + INTEGER*4 GDS__dsql_drop_func_failed + PARAMETER (GDS__dsql_drop_func_failed = 336397263) + INTEGER*4 GDS__dsql_recreate_func_failed + PARAMETER (GDS__dsql_recreate_func_failed = 336397264) + INTEGER*4 GDS__dsql_create_proc_failed + PARAMETER (GDS__dsql_create_proc_failed = 336397265) + INTEGER*4 GDS__dsql_alter_proc_failed + PARAMETER (GDS__dsql_alter_proc_failed = 336397266) + INTEGER*4 GDS__dsql_create_alter_proc_failed + PARAMETER (GDS__dsql_create_alter_proc_failed = 336397267) + INTEGER*4 GDS__dsql_drop_proc_failed + PARAMETER (GDS__dsql_drop_proc_failed = 336397268) + INTEGER*4 GDS__dsql_recreate_proc_failed + PARAMETER (GDS__dsql_recreate_proc_failed = 336397269) + INTEGER*4 GDS__dsql_create_trigger_failed + PARAMETER (GDS__dsql_create_trigger_failed = 336397270) + INTEGER*4 GDS__dsql_alter_trigger_failed + PARAMETER (GDS__dsql_alter_trigger_failed = 336397271) + INTEGER*4 GDS__dsql_create_alter_trigger_failed + PARAMETER (GDS__dsql_create_alter_trigger_failed = 336397272) + INTEGER*4 GDS__dsql_drop_trigger_failed + PARAMETER (GDS__dsql_drop_trigger_failed = 336397273) + INTEGER*4 GDS__dsql_recreate_trigger_failed + PARAMETER (GDS__dsql_recreate_trigger_failed = 336397274) + INTEGER*4 GDS__dsql_create_collation_failed + PARAMETER (GDS__dsql_create_collation_failed = 336397275) + INTEGER*4 GDS__dsql_drop_collation_failed + PARAMETER (GDS__dsql_drop_collation_failed = 336397276) + INTEGER*4 GDS__dsql_create_domain_failed + PARAMETER (GDS__dsql_create_domain_failed = 336397277) + INTEGER*4 GDS__dsql_alter_domain_failed + PARAMETER (GDS__dsql_alter_domain_failed = 336397278) + INTEGER*4 GDS__dsql_drop_domain_failed + PARAMETER (GDS__dsql_drop_domain_failed = 336397279) + INTEGER*4 GDS__dsql_create_except_failed + PARAMETER (GDS__dsql_create_except_failed = 336397280) + INTEGER*4 GDS__dsql_alter_except_failed + PARAMETER (GDS__dsql_alter_except_failed = 336397281) + INTEGER*4 GDS__dsql_create_alter_except_failed + PARAMETER (GDS__dsql_create_alter_except_failed = 336397282) + INTEGER*4 GDS__dsql_recreate_except_failed + PARAMETER (GDS__dsql_recreate_except_failed = 336397283) + INTEGER*4 GDS__dsql_drop_except_failed + PARAMETER (GDS__dsql_drop_except_failed = 336397284) + INTEGER*4 GDS__dsql_create_sequence_failed + PARAMETER (GDS__dsql_create_sequence_failed = 336397285) + INTEGER*4 GDS__dsql_create_table_failed + PARAMETER (GDS__dsql_create_table_failed = 336397286) + INTEGER*4 GDS__dsql_alter_table_failed + PARAMETER (GDS__dsql_alter_table_failed = 336397287) + INTEGER*4 GDS__dsql_drop_table_failed + PARAMETER (GDS__dsql_drop_table_failed = 336397288) + INTEGER*4 GDS__dsql_recreate_table_failed + PARAMETER (GDS__dsql_recreate_table_failed = 336397289) + INTEGER*4 GDS__dsql_create_pack_failed + PARAMETER (GDS__dsql_create_pack_failed = 336397290) + INTEGER*4 GDS__dsql_alter_pack_failed + PARAMETER (GDS__dsql_alter_pack_failed = 336397291) + INTEGER*4 GDS__dsql_create_alter_pack_failed + PARAMETER (GDS__dsql_create_alter_pack_failed = 336397292) + INTEGER*4 GDS__dsql_drop_pack_failed + PARAMETER (GDS__dsql_drop_pack_failed = 336397293) + INTEGER*4 GDS__dsql_recreate_pack_failed + PARAMETER (GDS__dsql_recreate_pack_failed = 336397294) + INTEGER*4 GDS__dsql_create_pack_body_failed + PARAMETER (GDS__dsql_create_pack_body_failed = 336397295) + INTEGER*4 GDS__dsql_drop_pack_body_failed + PARAMETER (GDS__dsql_drop_pack_body_failed = 336397296) + INTEGER*4 GDS__dsql_recreate_pack_body_failed + PARAMETER (GDS__dsql_recreate_pack_body_failed = 336397297) INTEGER*4 GDS__gsec_cant_open_db PARAMETER (GDS__gsec_cant_open_db = 336723983) INTEGER*4 GDS__gsec_switches_error Modified: firebird/trunk/lang_helpers/gds_codes.pas =================================================================== --- firebird/trunk/lang_helpers/gds_codes.pas 2010-07-26 02:14:48 UTC (rev 51367) +++ firebird/trunk/lang_helpers/gds_codes.pas 2010-07-26 02:37:57 UTC (rev 51368) @@ -1005,6 +1005,46 @@ gds_dsql_nostring_neg_dial3 = 336397255; gds_dsql_invalid_type_neg = 336397256; gds_dsql_max_distinct_items = 336397257; + gds_dsql_alter_charset_failed = 336397258; + gds_dsql_comment_on_failed = 336397259; + gds_dsql_create_func_failed = 336397260; + gds_dsql_alter_func_failed = 336397261; + gds_dsql_create_alter_func_failed = 336397262; + gds_dsql_drop_func_failed = 336397263; + gds_dsql_recreate_func_failed = 336397264; + gds_dsql_create_proc_failed = 336397265; + gds_dsql_alter_proc_failed = 336397266; + gds_dsql_create_alter_proc_failed = 336397267; + gds_dsql_drop_proc_failed = 336397268; + gds_dsql_recreate_proc_failed = 336397269; + gds_dsql_create_trigger_failed = 336397270; + gds_dsql_alter_trigger_failed = 336397271; + gds_dsql_create_alter_trigger_failed = 336397272; + gds_dsql_drop_trigger_failed = 336397273; + gds_dsql_recreate_trigger_failed = 336397274; + gds_dsql_create_collation_failed = 336397275; + gds_dsql_drop_collation_failed = 336397276; + gds_dsql_create_domain_failed = 336397277; + gds_dsql_alter_domain_failed = 336397278; + gds_dsql_drop_domain_failed = 336397279; + gds_dsql_create_except_failed = 336397280; + gds_dsql_alter_except_failed = 336397281; + gds_dsql_create_alter_except_failed = 336397282; + gds_dsql_recreate_except_failed = 336397283; + gds_dsql_drop_except_failed = 336397284; + gds_dsql_create_sequence_failed = 336397285; + gds_dsql_create_table_failed = 336397286; + gds_dsql_alter_table_failed = 336397287; + gds_dsql_drop_table_failed = 336397288; + gds_dsql_recreate_table_failed = 336397289; + gds_dsql_create_pack_failed = 336397290; + gds_dsql_alter_pack_failed = 336397291; + gds_dsql_create_alter_pack_failed = 336397292; + gds_dsql_drop_pack_failed = 336397293; + gds_dsql_recreate_pack_failed = 336397294; + gds_dsql_create_pack_body_failed = 336397295; + gds_dsql_drop_pack_body_failed = 336397296; + gds_dsql_recreate_pack_body_failed = 336397297; gds_gsec_cant_open_db = 336723983; gds_gsec_switches_error = 336723984; gds_gsec_no_op_spec = 336723985; Modified: firebird/trunk/src/common/classes/ByteChunk.h =================================================================== --- firebird/trunk/src/common/classes/ByteChunk.h 2010-07-26 02:14:48 UTC (rev 51367) +++ firebird/trunk/src/common/classes/ByteChunk.h 2010-07-26 02:37:57 UTC (rev 51368) @@ -24,6 +24,7 @@ #define COMMON_BYTE_CHUNK_H #include "../common/classes/array.h" +#include "../common/classes/fb_string.h" namespace Firebird { @@ -46,8 +47,22 @@ { } - const UCHAR* const data; - const size_t length; + // String buffer. + ByteChunk(string& str) + : data((UCHAR*) str.c_str()), + length(str.length()) + { + } + + // Empty. + ByteChunk() + : data(NULL), + length(0) + { + } + + const UCHAR* data; + size_t length; }; } // namespace Firebird Modified: firebird/trunk/src/common/classes/objects_array.h =================================================================== --- firebird/trunk/src/common/classes/objects_array.h 2010-07-26 02:14:48 UTC (rev 51367) +++ firebird/trunk/src/common/classes/objects_array.h 2010-07-26 02:37:57 UTC (rev 51368) @@ -279,6 +279,11 @@ size_t getCount() const {return inherited::getCount();} size_t getCapacity() const {return inherited::getCapacity();} + bool hasData() const + { + return getCount() != 0; + } + bool isEmpty() const { return getCount() == 0; Modified: firebird/trunk/src/dsql/BlrWriter.h =================================================================== --- firebird/trunk/src/dsql/BlrWriter.h 2010-07-26 02:14:48 UTC (rev 51367) +++ firebird/trunk/src/dsql/BlrWriter.h 2010-07-26 02:37:57 UTC (rev 51368) @@ -36,6 +36,9 @@ class BlrWriter : public Firebird::PermanentStorage { public: + typedef Firebird::HalfStaticArray<UCHAR, 1024> BlrData; + typedef Firebird::HalfStaticArray<UCHAR, 128> DebugData; + explicit BlrWriter(MemoryPool& p) : PermanentStorage(p), blrData(p), @@ -44,6 +47,10 @@ { } + virtual ~BlrWriter() + { + } + void appendUChar(const UCHAR byte) { blrData.add(byte); @@ -126,8 +133,8 @@ void putDebugArgument(UCHAR, USHORT, const TEXT*); void appendDebugInfo(); - Firebird::HalfStaticArray<UCHAR, 1024>& getBlrData() { return blrData; } - Firebird::HalfStaticArray<UCHAR, 128>& getDebugData() { return debugData; } + BlrData& getBlrData() { return blrData; } + DebugData& getDebugData() { return debugData; } ULONG getBaseOffset() const { return baseOffset; } void setBaseOffset(ULONG value) { baseOffset = value; } @@ -138,8 +145,8 @@ virtual bool isDdlDyn() = 0; private: - Firebird::HalfStaticArray<UCHAR, 1024> blrData; - Firebird::HalfStaticArray<UCHAR, 128> debugData; + BlrData blrData; + DebugData debugData; ULONG baseOffset; // place to go back and stuff in blr length }; Modified: firebird/trunk/src/dsql/DdlNodes.epp =================================================================== --- firebird/trunk/src/dsql/DdlNodes.epp 2010-07-26 02:14:48 UTC (rev 51367) +++ firebird/trunk/src/dsql/DdlNodes.epp 2010-07-26 02:37:57 UTC (rev 51368) @@ -19,10 +19,12 @@ */ #include "firebird.h" +#include "dyn_consts.h" #include "../jrd/common.h" #include "../dsql/DdlNodes.h" #include "../dsql/node.h" #include "../jrd/blr.h" +#include "../jrd/btr.h" #include "../jrd/dyn.h" #include "../jrd/flags.h" #include "../jrd/intl.h" @@ -30,6 +32,7 @@ #include "../jrd/msg_encode.h" #include "../jrd/obj.h" #include "../jrd/tra.h" +#include "../jrd/os/path_utils.h" #include "../jrd/IntlManager.h" #include "../jrd/PreparedStatement.h" #include "../jrd/blb_proto.h" @@ -39,7 +42,9 @@ #include "../jrd/dyn_ut_proto.h" #include "../jrd/exe_proto.h" #include "../jrd/intl_proto.h" +#include "../jrd/isc_f_proto.h" #include "../jrd/met_proto.h" +#include "../jrd/scl_proto.h" #include "../jrd/vio_proto.h" #include "../dsql/ddl_proto.h" #include "../dsql/errd_proto.h" @@ -49,14 +54,39 @@ #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); +static void checkForeignKeyTempScope(thread_db* tdbb, jrd_tra* transaction, + const MetaName& childRelName, const MetaName& masterIndexName); +static void checkSpTrigDependency(thread_db* tdbb, jrd_tra* transaction, + const MetaName& relationName, const MetaName& fieldName); +static void checkViewDependency(thread_db* tdbb, jrd_tra* transaction, + const MetaName& relationName, const MetaName& fieldName); +static void clearPermanentField(dsql_rel* relation, bool permanent); +static void deleteKeyConstraint(thread_db* tdbb, jrd_tra* transaction, + const MetaName& relationName, const MetaName& constraintName, const MetaName& indexName); +static bool fieldExists(thread_db* tdbb, jrd_tra* transaction, const MetaName& relationName, + const MetaName& fieldName); +static void makeRelationScopeName(const MetaName& name, const rel_t type, string& message); +static void modifyLocalFieldPosition(thread_db* tdbb, jrd_tra* transaction, + const MetaName& relationName, const MetaName& fieldName, USHORT newPosition, + USHORT existingPosition); +static rel_t relationType(SSHORT relationTypeNull, SSHORT relationType); +static void saveRelation(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, + const dsql_str* relationName, bool creating); +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); DATABASE DB = STATIC "ODS.RDB"; @@ -64,25 +94,376 @@ //---------------------- -// 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) +// Check temporary table reference rules between given child relation and master +// relation (owner of given PK/UK index). +static void checkForeignKeyTempScope(thread_db* tdbb, jrd_tra* transaction, + const MetaName& childRelName, const MetaName& masterIndexName) { - Arg::StatusVector newVector; - const ISC_STATUS* status = ex.value(); + AutoCacheRequest request(tdbb, drq_l_rel_info, DYN_REQUESTS); + bool error = false; + string master, child; - if (metaDataException) + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + RLC_M IN RDB$RELATION_CONSTRAINTS CROSS + REL_C IN RDB$RELATIONS CROSS + REL_M IN RDB$RELATIONS + WITH (RLC_M.RDB$CONSTRAINT_TYPE EQ UNIQUE_CNSTRT OR + RLC_M.RDB$CONSTRAINT_TYPE EQ PRIMARY_KEY) AND + RLC_M.RDB$INDEX_NAME EQ masterIndexName.c_str() AND + REL_C.RDB$RELATION_NAME EQ childRelName.c_str() AND + REL_M.RDB$RELATION_NAME EQ RLC_M.RDB$RELATION_NAME { - if (status[1] != isc_no_meta_update) - newVector << Arg::Gds(isc_no_meta_update); + const rel_t masterType = relationType(REL_M.RDB$RELATION_TYPE.NULL, REL_M.RDB$RELATION_TYPE); + fb_assert(masterType == rel_persistent || + masterType == rel_global_temp_preserve || + masterType == rel_global_temp_delete); - if (code != 0) - newVector << Arg::Gds(code); + const rel_t childType = relationType(REL_C.RDB$RELATION_TYPE.NULL, REL_C.RDB$RELATION_TYPE); + fb_assert(childType == rel_persistent || + childType == rel_global_temp_preserve || + childType == rel_global_temp_delete); + + error = masterType != childType && + !(masterType == rel_global_temp_preserve && childType == rel_global_temp_delete); + + if (error) + { + makeRelationScopeName(masterIndexName, masterType, master); + makeRelationScopeName(childRelName, childType, child); + } } + END_FOR - newVector.append(Arg::StatusVector(status)); - status_exception::raise(newVector); + if (error) + { + // Msg 232 : "%s can't reference %s" + status_exception::raise(Arg::Gds(ENCODE_ISC_MSG(232, DYN_MSG_FAC)) << child << master); + } } +// Check temporary table reference rules between just created child relation and all +// its master relations. +static void checkRelationTempScope(thread_db* tdbb, jrd_tra* transaction, + const MetaName& childRelName, const rel_t childType) +{ + if (childType != rel_persistent && + childType != rel_global_temp_preserve && + childType != rel_global_temp_delete) + { + return; + } + + AutoCacheRequest request(tdbb, drq_l_rel_info2, DYN_REQUESTS); + bool error = false; + string master, child; + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + RLC_C IN RDB$RELATION_CONSTRAINTS CROSS + IND_C IN RDB$INDICES CROSS + IND_M IN RDB$INDICES CROSS + REL_M IN RDB$RELATIONS + WITH RLC_C.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY AND + RLC_C.RDB$RELATION_NAME EQ childRelName.c_str() AND + IND_C.RDB$INDEX_NAME EQ RLC_C.RDB$INDEX_NAME AND + IND_M.RDB$INDEX_NAME EQ IND_C.RDB$FOREIGN_KEY AND + IND_M.RDB$RELATION_NAME EQ REL_M.RDB$RELATION_NAME + { + const rel_t masterType = relationType(REL_M.RDB$RELATION_TYPE.NULL, REL_M.RDB$RELATION_TYPE); + fb_assert(masterType == rel_persistent || + masterType == rel_global_temp_preserve || + masterType == rel_global_temp_delete); + + error = masterType != childType && + !(masterType == rel_global_temp_preserve && childType == rel_global_temp_delete); + + if (error) + { + makeRelationScopeName(REL_M.RDB$RELATION_NAME, masterType, master); + makeRelationScopeName(childRelName, childType, child); + } + } + END_FOR + + if (error) + { + // Msg 232 : "%s can't reference %s" + status_exception::raise(Arg::Gds(ENCODE_ISC_MSG(232, DYN_MSG_FAC)) << child << master); + } +} + +// Checks to see if the given field is referenced in a stored procedure or trigger. +// If the field is referenced, throw. +static void checkSpTrigDependency(thread_db* tdbb, jrd_tra* transaction, + const MetaName& relationName, const MetaName& fieldName) +{ + AutoRequest request; + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + FIRST 1 + DEP IN RDB$DEPENDENCIES + WITH DEP.RDB$DEPENDED_ON_NAME EQ relationName.c_str() AND + DEP.RDB$FIELD_NAME EQ fieldName.c_str() + { + MetaName depName(DEP.RDB$DEPENDENT_NAME); + + // msg 206: Column %s from table %s is referenced in %s. + status_exception::raise( + Arg::Gds(ENCODE_ISC_MSG(206, DYN_MSG_FAC)) << fieldName << relationName << depName); + } + END_FOR +} + +// Checks to see if the given field is referenced in a view. If it is, throw. +static void checkViewDependency(thread_db* tdbb, jrd_tra* transaction, + const MetaName& relationName, const MetaName& fieldName) +{ + AutoRequest request; + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + FIRST 1 + X IN RDB$RELATION_FIELDS CROSS + Y IN RDB$RELATION_FIELDS CROSS + Z IN RDB$VIEW_RELATIONS + WITH X.RDB$RELATION_NAME EQ relationName.c_str() AND + X.RDB$FIELD_NAME EQ fieldName.c_str() AND + X.RDB$FIELD_NAME EQ Y.RDB$BASE_FIELD AND + X.RDB$FIELD_SOURCE EQ Y.RDB$FIELD_SOURCE AND + Y.RDB$RELATION_NAME EQ Z.RDB$VIEW_NAME AND + X.RDB$RELATION_NAME EQ Z.RDB$RELATION_NAME AND + Y.RDB$VIEW_CONTEXT EQ Z.RDB$VIEW_CONTEXT + { + MetaName viewName(Z.RDB$VIEW_NAME); + + // msg 206: Column %s from table %s is referenced in %s. + status_exception::raise( + Arg::Gds(ENCODE_ISC_MSG(206, DYN_MSG_FAC)) << fieldName << relationName << viewName); + } + END_FOR +} + +// Removes temporary pool pointers from field, stored in permanent cache. +static void clearPermanentField(dsql_rel* relation, bool permanent) +{ + if (relation && relation->rel_fields && permanent) + { + relation->rel_fields->fld_procedure = NULL; + relation->rel_fields->fld_ranges = NULL; + relation->rel_fields->fld_character_set = NULL; + relation->rel_fields->fld_sub_type_name = NULL; + relation->rel_fields->fld_relation = relation; + } +} + +// Delete a record from RDB$RELATION_CONSTRAINTS based on a constraint name. +// +// On deleting from RDB$RELATION_CONSTRAINTS, 2 system triggers fire: +// +// (A) pre delete trigger: pre_delete_constraint, will: +// +// 1. delete a record first from RDB$REF_CONSTRAINTS where +// RDB$REF_CONSTRAINTS.RDB$CONSTRAINT_NAME = +// RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_NAME +// +// (B) post delete trigger: post_delete_constraint will: +// +// 1. also delete a record from RDB$INDICES where +// RDB$INDICES.RDB$INDEX_NAME = +// RDB$RELATION_CONSTRAINTS.RDB$INDEX_NAME +// +// 2. also delete a record from RDB$INDEX_SEGMENTS where +// RDB$INDEX_SEGMENTS.RDB$INDEX_NAME = +// RDB$RELATION_CONSTRAINTS.RDB$INDEX_NAME +static void deleteKeyConstraint(thread_db* tdbb, jrd_tra* transaction, + const MetaName& relationName, const MetaName& constraintName, const MetaName& indexName) +{ + SET_TDBB(tdbb); + + AutoCacheRequest request(tdbb, drq_e_rel_const, DYN_REQUESTS); + bool found = false; + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + RC IN RDB$RELATION_CONSTRAINTS + WITH RC.RDB$CONSTRAINT_NAME EQ constraintName.c_str() AND + RC.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY AND + RC.RDB$RELATION_NAME EQ relationName.c_str() AND + RC.RDB$INDEX_NAME EQ indexName.c_str() + { + found = true; + ERASE RC; + } + END_FOR + + if (!found) + { + // msg 130: "CONSTRAINT %s does not exist." + status_exception::raise( + Arg::Gds(ENCODE_ISC_MSG(130, DYN_MSG_FAC)) << constraintName); + } +} + +// Checks to see if the given field already exists in a relation. +static bool fieldExists(thread_db* tdbb, jrd_tra* transaction, const MetaName& relationName, + const MetaName& fieldName) +{ + AutoRequest request; + bool found = false; + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + FLD IN RDB$RELATION_FIELDS + WITH FLD.RDB$RELATION_NAME EQ relationName.c_str() AND + FLD.RDB$FIELD_NAME EQ fieldName.c_str() + { + found = true; + } + END_FOR + + return found; +} + +// Make string with relation name and type of its temporary scope. +static void makeRelationScopeName(const MetaName& name, const rel_t type, string& message) +{ + const char* scope = NULL; + + if (type == rel_global_temp_preserve) + scope = REL_SCOPE_GTT_PRESERVE; + else if (type == rel_global_temp_delete) + scope = REL_SCOPE_GTT_DELETE; + else + scope = REL_SCOPE_PERSISTENT; + + message.printf(scope, name.c_str()); +} + +// Alters the position of a field with respect to the +// other fields in the relation. This will only affect +// the order in which the fields will be returned when either +// viewing the relation or performing select * from the relation. +// +// The rules of engagement are as follows: +// if new_position > original position +// increase RDB$FIELD_POSITION for all fields with RDB$FIELD_POSITION +// between the new_position and existing position of the field +// then update the position of the field being altered. +// just update the position +// +// if new_position < original position +// decrease RDB$FIELD_POSITION for all fields with RDB$FIELD_POSITION +// between the new_position and existing position of the field +// then update the position of the field being altered. +// +// if new_position == original_position -- no_op +static void modifyLocalFieldPosition(thread_db* tdbb, jrd_tra* transaction, + const MetaName& relationName, const MetaName& fieldName, USHORT newPosition, + USHORT existingPosition) +{ + // Make sure that there are no duplicate field positions and no gaps in the position sequence. + // (gaps are introduced when fields are removed) + + AutoRequest request; + USHORT newPos = 0; + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + FLD IN RDB$RELATION_FIELDS + WITH FLD.RDB$RELATION_NAME EQ relationName.c_str() + SORTED BY ASCENDING FLD.RDB$FIELD_POSITION + { + if (FLD.RDB$FIELD_POSITION != newPos) + { + MODIFY FLD USING + FLD.RDB$FIELD_POSITION = newPos; + END_MODIFY + } + + ++newPos; + } + END_FOR + + // Find the position of the last field in the relation. + SLONG maxPosition = -1; + DYN_UTIL_generate_field_position(tdbb, NULL, relationName, &maxPosition); + + // If the existing position of the field is less than the new position of + // the field, subtract 1 to move the fields to their new positions otherwise, + // increase the value in RDB$FIELD_POSITION by one. + + bool moveDown = false; + if (existingPosition < newPosition) + moveDown = true; + + // Retrieve the records for the fields which have a position between the + // existing field position and the new field position. + + request.reset(); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + FLD IN RDB$RELATION_FIELDS + WITH FLD.RDB$RELATION_NAME EQ relationName.c_str() AND + FLD.RDB$FIELD_POSITION >= MIN(newPosition, existingPosition) AND + FLD.RDB$FIELD_POSITION <= MAX(newPosition, existingPosition) + { + MODIFY FLD USING + // If the field is the one we want, change the position, otherwise + // increase the value of RDB$FIELD_POSITION. + if (fieldName == FLD.RDB$FIELD_NAME) + { + if (newPosition > maxPosition) + { + // This prevents gaps in the position sequence of the fields. + FLD.RDB$FIELD_POSITION = maxPosition; + } + else + FLD.RDB$FIELD_POSITION = newPosition; + } + else + { + if (moveDown) + FLD.RDB$FIELD_POSITION = FLD.RDB$FIELD_POSITION - 1; + else + FLD.RDB$FIELD_POSITION = FLD.RDB$FIELD_POSITION + 1; + } + + FLD.RDB$FIELD_POSITION.NULL = FALSE; + END_MODIFY + } + END_FOR +} + +// Convert RDB$RELATION_TYPE to rel_t type. +static rel_t relationType(SSHORT relationTypeNull, SSHORT relationType) +{ + return relationTypeNull ? rel_persistent : rel_t(relationType); +} + +// Save the name of the relation or view currently being defined. This is done to support definition +// of triggers which will depend on the metadata created in this statement. +static void saveRelation(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, + const dsql_str* relationName, bool creating) +{ + //// TODO: Verify "creating" usage when using this function for views. + + DsqlCompiledStatement* statement = dsqlScratch->getStatement(); + + if (dsqlScratch->flags & DsqlCompilerScratch::FLAG_METADATA_SAVED) + return; + + dsqlScratch->flags |= DsqlCompilerScratch::FLAG_METADATA_SAVED; + + dsql_rel* relation; + + if (!creating) + relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, relationName); + else + { + MemoryPool& pool = *tdbb->getDefaultPool(); + relation = FB_NEW(pool) dsql_rel(pool); + relation->rel_name = relationName->str_data; + relation->rel_flags = REL_creating; + } + + dsqlScratch->relation = relation; +} + // Update RDB$FIELDS received by reference. static void updateRdbFields(const TypeClause& type, SSHORT& fieldType, @@ -169,6 +550,25 @@ //---------------------- +// Delete a security class. +bool DdlNode::deleteSecurityClass(thread_db* tdbb, jrd_tra* transaction, + const MetaName& secClass) +{ + AutoCacheRequest request(tdbb, drq_e_class, DYN_REQUESTS); + bool found = false; + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + SC IN RDB$SECURITY_CLASSES + WITH SC.RDB$SECURITY_CLASS EQ secClass.c_str() + { + found = true; + ERASE SC; + } + END_FOR + + return found; +} + void DdlNode::executeDdlTrigger(thread_db* tdbb, jrd_tra* transaction, DdlTriggerWhen when, int action, const MetaName& objectName, const string& sqlText) { @@ -192,14 +592,12 @@ 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 @@ -300,51 +698,105 @@ } } - void DdlNode::resetContextStack() { dsqlScratch->context->clear(); dsqlScratch->contextNumber = 0; } - -void DdlNode::storeGlobalField(thread_db* tdbb, jrd_tra* transaction, const TypeClause& field, - MetaName& name) +void DdlNode::storeGlobalField(thread_db* tdbb, jrd_tra* transaction, MetaName& name, + const TypeClause& field, const string& computedSource, const BlrWriter::BlrData& computedValue) { - bool endStore = false; + Attachment* attachment = transaction->tra_attachment; - try + const dsql_nod* elements = field.legacyField->fld_ranges; + const USHORT dims = elements ? elements->nod_count / 2 : 0; + + if (dims > MAX_ARRAY_DIMENSIONS) { - if (name.isEmpty()) - DYN_UTIL_generate_field_name(tdbb, NULL, name); + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-604) << + Arg::Gds(isc_dsql_max_arr_dim_exceeded)); + } - AutoCacheRequest requestHandle(tdbb, drq_s_fld_src, DYN_REQUESTS); + if (name.isEmpty()) + DYN_UTIL_generate_field_name(tdbb, NULL, name); - STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) - FLD IN RDB$FIELDS + AutoCacheRequest requestHandle(tdbb, drq_s_fld_src, DYN_REQUESTS); + + STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + FLD IN RDB$FIELDS + { + FLD.RDB$SYSTEM_FLAG = 0; + strcpy(FLD.RDB$FIELD_NAME, name.c_str()); + + FLD.RDB$COMPUTED_SOURCE.NULL = TRUE; + FLD.RDB$COMPUTED_BLR.NULL = TRUE; + FLD.RDB$DIMENSIONS.NULL = TRUE; + + 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); + + if (dims != 0) { - FLD.RDB$SYSTEM_FLAG = 0; - strcpy(FLD.RDB$FIELD_NAME, name.c_str()); + FLD.RDB$DIMENSIONS.NULL = FALSE; + FLD.RDB$DIMENSIONS = dims; + } - 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); + if (computedSource.hasData()) + { + FLD.RDB$COMPUTED_SOURCE.NULL = FALSE; + attachment->storeMetaDataBlob(tdbb, transaction, &FLD.RDB$COMPUTED_SOURCE, + computedSource); + } - endStore = true; + if (computedValue.hasData()) + { + FLD.RDB$COMPUTED_BLR.NULL = FALSE; + attachment->storeBinaryBlob(tdbb, transaction, &FLD.RDB$COMPUTED_BLR, + computedValue); } - END_STORE } - catch (const status_exception& ex) + END_STORE + + if (elements) // Is the type an array? { - // STORE RDB$FIELDS failed - rethrowMetaException(ex, ENCODE_ISC_MSG(13, DYN_MSG_FAC), endStore); + 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)); + } + + STORE (REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + DIM IN RDB$FIELD_DIMENSIONS + { + strcpy(DIM.RDB$FIELD_NAME, name.c_str()); + DIM.RDB$DIMENSION = position; + DIM.RDB$UPPER_BOUND = hrange; + DIM.RDB$LOWER_BOUND = lrange; + } + END_STORE + } } } @@ -358,11 +810,10 @@ { } - -void TypeClause::resolve(DsqlCompilerScratch* dsqlScratch) +void TypeClause::resolve(DsqlCompilerScratch* dsqlScratch, bool modifying) { - DDL_resolve_intl_type(dsqlScratch, legacyField, - (collate.isEmpty() ? NULL : MAKE_cstring(collate.c_str()))); + DDL_resolve_intl_type2(dsqlScratch, legacyField, + (collate.isEmpty() ? NULL : MAKE_cstring(collate.c_str())), modifying); type = legacyField->fld_dtype; length = legacyField->fld_length; @@ -385,7 +836,6 @@ typeOfName = legacyField->fld_type_of_name; } - void TypeClause::print(string& text) const { text.printf("typeOfTable: '%s' typeOfName: '%s' notNull: %d fieldSource: '%s'", @@ -405,7 +855,6 @@ { } - void ParameterClause::print(string& text) const { string s; @@ -426,7 +875,6 @@ charSet.c_str(), defaultCollation.c_str()); } - void AlterCharSetNode::execute(thread_db* tdbb, jrd_tra* transaction) { METD_drop_charset(transaction, charSet); @@ -467,16 +915,12 @@ END_FOR if (!charSetFound) - { - status_exception::raise(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_charset_not_found) << Arg::Str(charSet)); - } + status_exception::raise(Arg::Gds(isc_charset_not_found) << Arg::Str(charSet)); if (!collationFound) { - status_exception::raise(Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_collation_not_found) << Arg::Str(defaultCollation) << - Arg::Str(charSet)); + status_exception::raise( + Arg::Gds(isc_collation_not_found) << Arg::Str(defaultCollation) << Arg::Str(charSet)); } executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_CHARACTER_SET, charSet); @@ -496,7 +940,6 @@ 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. @@ -691,7 +1134,6 @@ } } - DdlNode* CreateAlterFunctionNode::internalDsqlPass() { DsqlCompiledStatement* const statement = dsqlScratch->getStatement(); @@ -762,7 +1204,6 @@ return DdlNode::internalDsqlPass(); } - void CreateAlterFunctionNode::execute(thread_db* tdbb, jrd_tra* transaction) { fb_assert(create || alter); @@ -781,15 +1222,9 @@ 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)); - } + status_exception::raise(Arg::Gds(isc_dyn_func_not_found) << Arg::Str(name)); } } else @@ -817,7 +1252,6 @@ } } - void CreateAlterFunctionNode::executeCreate(thread_db* tdbb, jrd_tra* transaction) { Attachment* attachment = transaction->getAttachment(); @@ -918,7 +1352,6 @@ executeAlter(tdbb, transaction, false, false); } - bool CreateAlterFunctionNode::executeAlter(thread_db* tdbb, jrd_tra* transaction, bool secondPass, bool runTriggers) { @@ -935,9 +1368,8 @@ { 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); + status_exception::raise( + Arg::Gds(isc_dyn_cannot_mod_sysfunc) << FUN.RDB$FUNCTION_NAME); } if (!secondPass && runTriggers && package.isEmpty()) @@ -1031,7 +1463,6 @@ return modified; } - void CreateAlterFunctionNode::storeArgument(thread_db* tdbb, jrd_tra* transaction, unsigned pos, const ParameterClause& parameter) { @@ -1099,7 +1530,7 @@ else { MetaName fieldName; - storeGlobalField(tdbb, transaction, parameter, fieldName); + storeGlobalField(tdbb, transaction, fieldName, parameter); strcpy(ARG.RDB$FIELD_SOURCE, fieldName.c_str()); } } @@ -1155,7 +1586,6 @@ END_STORE } - void CreateAlterFunctionNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/) { if (invalid) @@ -1327,7 +1757,6 @@ END_FOR } - void DropFunctionNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const { text.printf( @@ -1336,14 +1765,12 @@ 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 @@ -1360,10 +1787,7 @@ 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); - } + status_exception::raise(Arg::Gds(isc_dyn_cannot_mod_sysfunc) << FUN.RDB$FUNCTION_NAME); if (package.isEmpty()) executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_FUNCTION, name); @@ -1371,18 +1795,14 @@ ERASE FUN; if (!FUN.RDB$SECURITY_CLASS.NULL) - DYN_delete_security_class2(transaction, FUN.RDB$SECURITY_CLASS); + deleteSecurityClass(tdbb, 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)); - } + status_exception::raise(Arg::Gds(isc_dyn_func_not_found) << Arg::Str(name)); if (package.isEmpty()) { @@ -1426,7 +1846,6 @@ text.printf("RecreateFunctionNode\n"); } - DdlNode* RecreateFunctionNode::internalDsqlPass() { createNode->dsqlPass(dsqlScratch); @@ -1434,14 +1853,13 @@ 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); + dropNode.execute(tdbb, transaction); + createNode->execute(tdbb, transaction); savePoint.release(); // everything is ok } @@ -1488,7 +1906,6 @@ } } - DdlNode* CreateAlterProcedureNode::internalDsqlPass() { DsqlCompiledStatement* statement = dsqlScratch->getStatement(); @@ -1565,7 +1982,6 @@ return DdlNode::internalDsqlPass(); } - void CreateAlterProcedureNode::execute(thread_db* tdbb, jrd_tra* transaction) { fb_assert(create || alter); @@ -1584,11 +2000,7 @@ 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)); - } + status_exception::raise(Arg::Gds(isc_dyn_proc_not_found) << Arg::Str(name)); } } else @@ -1614,7 +2026,6 @@ } } - void CreateAlterProcedureNode::executeCreate(thread_db* tdbb, jrd_tra* transaction) { Attachment* attachment = transaction->getAttachment(); @@ -1704,7 +2115,6 @@ executeAlter(tdbb, transaction, false, false); } - bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, jrd_tra* transaction, bool secondPass, bool runTriggers) { @@ -1721,8 +2131,7 @@ { 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); + status_exception::raise(Arg::Gds(isc_dyn_cannot_mod_sysproc) << P.RDB$PROCEDURE_NAME); } if (!secondPass && runTriggers && package.isEmpty()) @@ -1852,7 +2261,6 @@ return modified; } - void CreateAlterProcedureNode::storeParameter(thread_db* tdbb, jrd_tra* transaction, USHORT type, unsigned pos, const ParameterClause& parameter) { @@ -1905,7 +2313,7 @@ else { MetaName fieldName; - storeGlobalField(tdbb, transaction, parameter, fieldName); + storeGlobalField(tdbb, transaction, fieldName, parameter); strcpy(PRM.RDB$FIELD_SOURCE, fieldName.c_str()); } } @@ -1958,7 +2366,6 @@ END_STORE } - void CreateAlterProcedureNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/) { if (invalid) @@ -2132,7 +2539,6 @@ END_FOR } - void DropProcedureNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const { text.printf( @@ -2141,14 +2547,12 @@ 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 @@ -2165,10 +2569,7 @@ 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); - } + status_exception::raise(Arg::Gds(isc_dyn_cannot_mod_sysproc) << PRC.RDB$PROCEDURE_NAME); if (package.isEmpty()) executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_PROCEDURE, name); @@ -2176,18 +2577,14 @@ ERASE PRC; if (!PRC.RDB$SECURITY_CLASS.NULL) - DYN_delete_security_class2(transaction, PRC.RDB$SECURITY_CLASS); + deleteSecurityClass(tdbb, 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)); - } + status_exception::raise(Arg::Gds(isc_dyn_proc_not_found) << Arg::Str(name)); if (package.isEmpty()) { @@ -2231,7 +2628,6 @@ text.printf("RecreateProcedureNode\n"); } - DdlNode* RecreateProcedureNode::internalDsqlPass() { dropNode.dsqlPass(dsqlScratch); @@ -2239,14 +2635,13 @@ 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); + dropNode.execute(tdbb, transaction); + createNode->execute(tdbb, transaction); savePoint.release(); // everything is ok } @@ -2255,15 +2650,156 @@ //---------------------- +void TriggerDefinition::store(thread_db* tdbb, jrd_tra* transaction) +{ + if (name.isEmpty()) + DYN_UTIL_generate_trigger_name(tdbb, transaction, name); + + AutoCacheRequest requestHandle(tdbb, drq_s_triggers2, DYN_REQUESTS); + + STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + TRG IN RDB$TRIGGERS + { + TRG.RDB$SYSTEM_FLAG = SSHORT(systemFlag); + TRG.RDB$FLAGS = TRG_sql | (fkTrigger ? TRG_ignore_perm : 0); + 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); + } + END_STORE + + modify(tdbb, transaction); +} + +bool TriggerDefinition::modify(thread_db* tdbb, jrd_tra* transaction) +{ + 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); + + 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 && + TRG.RDB$RELATION_NAME.NULL) + { + status_exception::raise( + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_dsql_db_trigger_type_cant_change)); + } + + if (systemFlag == fb_sysflag_user && !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_dyn_cant_modify_auto_trig)); + break; + + case fb_sysflag_system: + status_exception::raise( + Arg::Gds(isc_dyn_cannot_mod_systrig) << TRG.RDB$TRIGGER_NAME); + break; + + default: + break; + } + } + + preModify(tdbb, transaction); + + MODIFY TRG + if (blrData.length > 0 || external) + { + fb_assert(!(blrData.length > 0 && 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 (blrData.length > 0) + { + TRG.RDB$TRIGGER_BLR.NULL = FALSE; + attachment->storeBinaryBlob(tdbb, transaction, &TRG.RDB$TRIGGER_BLR, blrData); + } + + if (debugData.length > 0) + { + TRG.RDB$DEBUG_INFO.NULL = FALSE; + attachment->storeBinaryBlob(tdbb, transaction, &TRG.RDB$DEBUG_INFO, debugData); + } + + if (source.hasData()) + { + TRG.RDB$TRIGGER_SOURCE.NULL = FALSE; + attachment->storeMetaDataBlob(tdbb, transaction, &TRG.RDB$TRIGGER_SOURCE, source); + } + + TRG.RDB$VALID_BLR = TRUE; + + modified = true; + END_MODIFY + } + END_FOR + + if (modified) + postModify(tdbb, transaction); + + return modified; +} + + +//---------------------- + + 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); + name.c_str(), create, alter, relationName.c_str(), type.specified, type.value, + active.specified, active.value, position.specified, position.value); if (external) { @@ -2274,7 +2810,6 @@ } } - DdlNode* CreateAlterTriggerNode::internalDsqlPass() { DsqlCompiledStatement* statement = dsqlScratch->getStatement(); @@ -2299,7 +2834,6 @@ return DdlNode::internalDsqlPass(); } - void CreateAlterTriggerNode::execute(thread_db* tdbb, jrd_tra* transaction) { fb_assert(create || alter); @@ -2331,27 +2865,22 @@ END_FOR if (!type.specified) - { - status_exception::raise( - Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_dyn_trig_not_found) << Arg::Str(name)); - } + status_exception::raise(Arg::Gds(isc_dyn_trig_not_found) << Arg::Str(name)); } compile(tdbb, transaction); + blrData = dsqlScratch->getBlrData(); + debugData = dsqlScratch->getDebugData(); + if (alter) { - if (!executeAlter(tdbb, transaction, true)) + if (!modify(tdbb, transaction)) { 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)); - } + status_exception::raise(Arg::Gds(isc_dyn_trig_not_found) << Arg::Str(name)); } } else @@ -2360,166 +2889,13 @@ 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); - + store(tdbb, transaction); executeDdlTrigger(tdbb, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_TRIGGER, name); } - -bool CreateAlterTriggerNode::executeAlter(thread_db* tdbb, jrd_tra* transaction, bool runTriggers) -{ - 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, - dsqlScratch->getBlrData()); - - TRG.RDB$DEBUG_INFO.NULL = FALSE; - attachment->storeBinaryBlob(tdbb, transaction, &TRG.RDB$DEBUG_INFO, - 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; - - modified = true; - END_MODIFY - } - 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; -} - - void CreateAlterTriggerNode::compile(thread_db* tdbb, jrd_tra* /*transaction*/) { if (invalid) @@ -2629,14 +3005,12 @@ 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 @@ -2657,13 +3031,12 @@ 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)); + status_exception::raise(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); + status_exception::raise( + Arg::Gds(isc_dyn_cannot_mod_systrig) << X.RDB$TRIGGER_NAME); break; default: @@ -2683,11 +3056,7 @@ END_FOR if (!found && !silent) - { - status_exception::raise( - Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_dyn_trig_not_found) << Arg::Str(name)); - } + status_exception::raise(Arg::Gds(isc_dyn_trig_not_found) << Arg::Str(name)); requestHandle.reset(tdbb, drq_e_trg_msgs3, DYN_REQUESTS); @@ -2758,7 +3127,6 @@ text.printf("RecreateTriggerNode\n"); } - DdlNode* RecreateTriggerNode::internalDsqlPass() { dropNode.dsqlPass(dsqlScratch); @@ -2766,14 +3134,13 @@ 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); + dropNode.execute(tdbb, transaction); + createNode->execute(tdbb, transaction); savePoint.release(); // everything is ok } @@ -2805,170 +3172,147 @@ executeDdlTrigger(tdbb, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_COLLATION, name); - bool endStore = false; + AutoCacheRequest request(tdbb, drq_s_colls, DYN_REQUESTS); - try + STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + X IN RDB$COLLATIONS { - AutoCacheRequest request(tdbb, drq_s_colls, DYN_REQUESTS); + X.RDB$CHARACTER_SET_ID = forCharSetId; + strcpy(X.RDB$COLLATION_NAME, name.c_str()); + X.RDB$SYSTEM_FLAG = 0; - 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; - X.RDB$SPECIFIC_ATTRIBUTES.NULL = TRUE; - X.RDB$BASE_COLLATION_NAME.NULL = TRUE; + CharSet* cs = INTL_charset_lookup(tdbb, forCharSetId); + SubtypeInfo info; - CharSet* cs = INTL_charset_lookup(tdbb, forCharSetId); - SubtypeInfo info; - - if (fromName.hasData()) + if (fromName.hasData()) + { + if (MET_get_char_coll_subtype_info(tdbb, + INTL_CS_COLL_TO_TTYPE(forCharSetId, fromCollationId), &info) && + info.specificAttributes.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(); + ULONG size = info.specificAttributes.getCount() * cs->maxBytesPerChar(); size = INTL_convert_bytes(tdbb, forCharSetId, temp.getBuffer(size), size, - attachment->att_charset, specificAttributes.begin(), - specificAttributes.getCount(), status_exception::raise); + CS_METADATA, info.specificAttributes.begin(), + info.specificAttributes.getCount(), status_exception::raise); temp.shrink(size); - specificAttributes = temp; + info.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; + 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 (!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); - } + if (specificAttributes.hasData()) + ... [truncated message content] |