From: <asf...@us...> - 2010-08-02 02:22:33
|
Revision: 51383 http://firebird.svn.sourceforge.net/firebird/?rev=51383&view=rev Author: asfernandes Date: 2010-08-02 02:22:26 +0000 (Mon, 02 Aug 2010) Log Message: ----------- Refactor CREATE/ALTER/CREATE OR ALTER/RECREATE VIEW and cleanup related to previously refactors Modified Paths: -------------- firebird/trunk/lang_helpers/gds_codes.ftn firebird/trunk/lang_helpers/gds_codes.pas firebird/trunk/src/dsql/DdlNodes.epp firebird/trunk/src/dsql/DdlNodes.h firebird/trunk/src/dsql/ddl.cpp firebird/trunk/src/dsql/dsql.h firebird/trunk/src/dsql/metd.epp firebird/trunk/src/dsql/metd_proto.h firebird/trunk/src/dsql/node.h firebird/trunk/src/dsql/parse.y firebird/trunk/src/dsql/pass1.cpp 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/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-08-01 20:43:29 UTC (rev 51382) +++ firebird/trunk/lang_helpers/gds_codes.ftn 2010-08-02 02:22:26 UTC (rev 51383) @@ -2076,6 +2076,16 @@ 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__dsql_create_view_failed + PARAMETER (GDS__dsql_create_view_failed = 336397298) + INTEGER*4 GDS__dsql_alter_view_failed + PARAMETER (GDS__dsql_alter_view_failed = 336397299) + INTEGER*4 GDS__dsql_create_alter_view_failed + PARAMETER (GDS__dsql_create_alter_view_failed = 336397300) + INTEGER*4 GDS__dsql_recreate_view_failed + PARAMETER (GDS__dsql_recreate_view_failed = 336397301) + INTEGER*4 GDS__dsql_drop_view_failed + PARAMETER (GDS__dsql_drop_view_failed = 336397302) 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-08-01 20:43:29 UTC (rev 51382) +++ firebird/trunk/lang_helpers/gds_codes.pas 2010-08-02 02:22:26 UTC (rev 51383) @@ -1045,6 +1045,11 @@ gds_dsql_create_pack_body_failed = 336397295; gds_dsql_drop_pack_body_failed = 336397296; gds_dsql_recreate_pack_body_failed = 336397297; + gds_dsql_create_view_failed = 336397298; + gds_dsql_alter_view_failed = 336397299; + gds_dsql_create_alter_view_failed = 336397300; + gds_dsql_recreate_view_failed = 336397301; + gds_dsql_drop_view_failed = 336397302; gds_gsec_cant_open_db = 336723983; gds_gsec_switches_error = 336723984; gds_gsec_no_op_spec = 336723985; Modified: firebird/trunk/src/dsql/DdlNodes.epp =================================================================== --- firebird/trunk/src/dsql/DdlNodes.epp 2010-08-01 20:43:29 UTC (rev 51382) +++ firebird/trunk/src/dsql/DdlNodes.epp 2010-08-02 02:22:26 UTC (rev 51383) @@ -75,8 +75,9 @@ const MetaName& relationName, const MetaName& fieldName, USHORT newPosition, USHORT existingPosition); static rel_t relationType(SSHORT relationTypeNull, SSHORT relationType); +static void saveField(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const MetaName& fieldName); static void saveRelation(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - const dsql_str* relationName, bool creating); + const MetaName& relationName, bool view, bool creating); static void updateRdbFields(const TypeClause& type, SSHORT& fieldType, SSHORT& fieldLength, @@ -435,13 +436,27 @@ return relationTypeNull ? rel_persistent : rel_t(relationType); } +// Save the name of a field in 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 saveField(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const MetaName& fieldName) +{ + dsql_rel* relation = dsqlScratch->relation; + if (!relation) + return; + + MemoryPool& p = relation->rel_flags & REL_new_relation ? + *tdbb->getDefaultPool() : dsqlScratch->getAttachment()->dbb_pool; + dsql_fld* field = FB_NEW(p) dsql_fld(p); + field->fld_name = fieldName.c_str(); + field->fld_next = relation->rel_fields; + relation->rel_fields = field; +} + // 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) + const MetaName& relationName, bool view, bool creating) { - //// TODO: Verify "creating" usage when using this function for views. - DsqlCompiledStatement* statement = dsqlScratch->getStatement(); if (dsqlScratch->flags & DsqlCompilerScratch::FLAG_METADATA_SAVED) @@ -451,14 +466,15 @@ dsql_rel* relation; - if (!creating) + if (!view && !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; + relation->rel_name = relationName; + if (!view) + relation->rel_flags = REL_creating; } dsqlScratch->relation = relation; @@ -511,8 +527,11 @@ fieldScaleNull = FALSE; fieldScale = 0; - characterLengthNull = FALSE; - characterLength = type.charLength; + if (type.charLength != 0) + { + characterLengthNull = FALSE; + characterLength = type.charLength; + } characterSetIdNull = FALSE; characterSetId = type.charSetId; @@ -815,6 +834,11 @@ DDL_resolve_intl_type2(dsqlScratch, legacyField, (collate.isEmpty() ? NULL : MAKE_cstring(collate.c_str())), modifying); + setup(dsqlScratch); +} + +void TypeClause::setup(DsqlCompilerScratch* dsqlScratch) +{ type = legacyField->fld_dtype; length = legacyField->fld_length; scale = legacyField->fld_scale; @@ -2655,7 +2679,7 @@ if (name.isEmpty()) DYN_UTIL_generate_trigger_name(tdbb, transaction, name); - AutoCacheRequest requestHandle(tdbb, drq_s_triggers2, DYN_REQUESTS); + AutoCacheRequest requestHandle(tdbb, drq_s_triggers, DYN_REQUESTS); STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) TRG IN RDB$TRIGGERS @@ -3403,7 +3427,8 @@ 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 + 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 @@ -4205,7 +4230,7 @@ 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 + 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. @@ -4673,6 +4698,175 @@ //---------------------- +void RelationNode::FieldDefinition::modify(thread_db* tdbb, jrd_tra* transaction) +{ + Attachment* attachment = transaction->tra_attachment; + + AutoRequest request; + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + RFR IN RDB$RELATION_FIELDS + WITH RFR.RDB$RELATION_NAME EQ relationName.c_str() AND + RFR.RDB$FIELD_NAME EQ name.c_str() + { + // ASF: This is prepared only to modify view fields! + + MODIFY RFR + strcpy(RFR.RDB$FIELD_SOURCE, fieldSource.c_str()); + + RFR.RDB$COLLATION_ID.NULL = TRUE; + RFR.RDB$GENERATOR_NAME.NULL = TRUE; + RFR.RDB$IDENTITY_TYPE.NULL = TRUE; + RFR.RDB$NULL_FLAG.NULL = TRUE; + RFR.RDB$DEFAULT_SOURCE.NULL = TRUE; + RFR.RDB$DEFAULT_VALUE.NULL = TRUE; + RFR.RDB$FIELD_POSITION.NULL = TRUE; + + RFR.RDB$VIEW_CONTEXT.NULL = TRUE; + RFR.RDB$BASE_FIELD.NULL = TRUE; + ///RFR.RDB$UPDATE_FLAG.NULL = TRUE; + + if (collationId.specified) + { + RFR.RDB$COLLATION_ID.NULL = FALSE; + RFR.RDB$COLLATION_ID = collationId.value; + } + + SLONG fieldPos = -1; + + if (position.specified) + fieldPos = position.value; + else + { + DYN_UTIL_generate_field_position(tdbb, NULL, name, &fieldPos); + if (fieldPos >= 0) + ++fieldPos; + } + + if (fieldPos >= 0) + { + RFR.RDB$FIELD_POSITION.NULL = FALSE; + RFR.RDB$FIELD_POSITION = SSHORT(fieldPos); + } + + if (baseField.hasData()) + { + RFR.RDB$BASE_FIELD.NULL = FALSE; + strcpy(RFR.RDB$BASE_FIELD, baseField.c_str()); + } + + if (viewContext.specified) + { + fb_assert(baseField.hasData()); + + RFR.RDB$VIEW_CONTEXT.NULL = FALSE; + RFR.RDB$VIEW_CONTEXT = viewContext.value; + + DYN_UTIL_find_field_source(tdbb, transaction, relationName, viewContext.value, + baseField.c_str(), RFR.RDB$FIELD_SOURCE); + } + END_MODIFY + } + END_FOR +} + +void RelationNode::FieldDefinition::store(thread_db* tdbb, jrd_tra* transaction) +{ + Attachment* attachment = transaction->tra_attachment; + + AutoCacheRequest request(tdbb, drq_s_lfields, DYN_REQUESTS); + + STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + RFR IN RDB$RELATION_FIELDS + { + strcpy(RFR.RDB$FIELD_NAME, name.c_str()); + strcpy(RFR.RDB$RELATION_NAME, relationName.c_str()); + strcpy(RFR.RDB$FIELD_SOURCE, fieldSource.c_str()); + RFR.RDB$SYSTEM_FLAG = 0; + + RFR.RDB$COLLATION_ID.NULL = TRUE; + RFR.RDB$GENERATOR_NAME.NULL = TRUE; + RFR.RDB$IDENTITY_TYPE.NULL = TRUE; + RFR.RDB$NULL_FLAG.NULL = TRUE; + RFR.RDB$DEFAULT_SOURCE.NULL = TRUE; + RFR.RDB$DEFAULT_VALUE.NULL = TRUE; + RFR.RDB$FIELD_POSITION.NULL = TRUE; + + RFR.RDB$VIEW_CONTEXT.NULL = TRUE; + RFR.RDB$BASE_FIELD.NULL = TRUE; + ///RFR.RDB$UPDATE_FLAG.NULL = TRUE; + + if (collationId.specified) + { + RFR.RDB$COLLATION_ID.NULL = FALSE; + RFR.RDB$COLLATION_ID = collationId.value; + } + + if (identitySequence.hasData()) + { + RFR.RDB$GENERATOR_NAME.NULL = FALSE; + strcpy(RFR.RDB$GENERATOR_NAME, identitySequence.c_str()); + + RFR.RDB$IDENTITY_TYPE.NULL = FALSE; + RFR.RDB$IDENTITY_TYPE = IDENT_TYPE_BY_DEFAULT; + } + + if (notNullFlag.specified) + { + RFR.RDB$NULL_FLAG.NULL = FALSE; + RFR.RDB$NULL_FLAG = notNullFlag.value; + } + + if (defaultSource.hasData()) + { + RFR.RDB$DEFAULT_SOURCE.NULL = FALSE; + attachment->storeMetaDataBlob(tdbb, transaction, &RFR.RDB$DEFAULT_SOURCE, + defaultSource); + } + + if (defaultValue.length > 0) + { + RFR.RDB$DEFAULT_VALUE.NULL = FALSE; + attachment->storeBinaryBlob(tdbb, transaction, &RFR.RDB$DEFAULT_VALUE, defaultValue); + } + + SLONG fieldPos = -1; + + if (position.specified) + fieldPos = position.value; + else + { + DYN_UTIL_generate_field_position(tdbb, NULL, name, &fieldPos); + if (fieldPos >= 0) + ++fieldPos; + } + + if (fieldPos >= 0) + { + RFR.RDB$FIELD_POSITION.NULL = FALSE; + RFR.RDB$FIELD_POSITION = SSHORT(fieldPos); + } + + if (baseField.hasData()) + { + RFR.RDB$BASE_FIELD.NULL = FALSE; + strcpy(RFR.RDB$BASE_FIELD, baseField.c_str()); + } + + if (viewContext.specified) + { + fb_assert(baseField.hasData()); + + RFR.RDB$VIEW_CONTEXT.NULL = FALSE; + RFR.RDB$VIEW_CONTEXT = viewContext.value; + + DYN_UTIL_find_field_source(tdbb, transaction, relationName, viewContext.value, + baseField.c_str(), RFR.RDB$FIELD_SOURCE); + } + } + END_STORE +} + // Delete local field. // // The rules for dropping a regular column: @@ -4828,6 +5022,29 @@ } } +void RelationNode::storePrivileges(thread_db* tdbb, jrd_tra* transaction) +{ + Attachment* attachment = transaction->tra_attachment; + + AutoCacheRequest request(tdbb, drq_s_usr_prvs, DYN_REQUESTS); + + for (const TEXT* p = ALL_PRIVILEGES; *p; ++p) + { + STORE(REQUEST_HANDLE request 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_relation; + X.RDB$PRIVILEGE[0] = *p; + X.RDB$PRIVILEGE[1] = 0; + X.RDB$GRANT_OPTION = 1; + } + END_STORE + } +} + void RelationNode::defineField(thread_db* tdbb, jrd_tra* transaction, const dsql_nod* element, SSHORT position, const dsql_nod* pkCols) { @@ -4887,157 +5104,109 @@ } } - AutoCacheRequest request(tdbb, drq_s_lfields2, DYN_REQUESTS); + FieldDefinition fieldDefinition(*tdbb->getDefaultPool()); + fieldDefinition.relationName = name; + fieldDefinition.name = field->fld_name; + fieldDefinition.notNullFlag = notNullFlag; - STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - RFR IN RDB$RELATION_FIELDS - { - const dsql_nod* domainNode = element->nod_arg[Dsql::e_dfl_domain]; - MetaName fieldSource; + if (position >= 0) + fieldDefinition.position = position; - if (domainNode) - { - const dsql_nod* node1 = domainNode->nod_arg[Dsql::e_dom_name]; - const dsql_str* domainName = (dsql_str*) node1->nod_arg[Dsql::e_fln_name]; + const dsql_nod* domainNode = element->nod_arg[Dsql::e_dfl_domain]; - // Get the domain information. - if (!METD_get_domain(transaction, field, domainName->str_data)) - { - // 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) << domainName->str_data); - } + if (domainNode) + { + const dsql_nod* node1 = domainNode->nod_arg[Dsql::e_dom_name]; + const dsql_str* domainName = (dsql_str*) node1->nod_arg[Dsql::e_fln_name]; - fieldSource = domainName->str_data; - } - else + // Get the domain information. + if (!METD_get_domain(transaction, field, domainName->str_data)) { - string computedSource; - BlrWriter::BlrData computedValue; - - if (element->nod_arg[e_dfl_computed]) - { - field->fld_flags |= FLD_computed; - - defineComputed(tdbb, field, element->nod_arg[e_dfl_computed], - computedSource, computedValue); - } - - TypeClause fieldType(field, NULL); // Don't use the collate in the generated domain. - fieldType.resolve(dsqlScratch); - - // Generate a domain. - storeGlobalField(tdbb, transaction, fieldSource, fieldType, - computedSource, computedValue); - } - - if ((relation->rel_flags & REL_external) && - (field->fld_dtype == dtype_blob || field->fld_dtype == dtype_array || - field->fld_dimensions)) - { - const char* typeName = (field->fld_dtype == dtype_blob ? "BLOB" : "ARRAY"); - + // 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_type_not_supp_ext_tab) << typeName << name << field->fld_name); + Arg::Gds(isc_dsql_domain_not_found) << domainName->str_data); } - strcpy(RFR.RDB$FIELD_NAME, field->fld_name.c_str()); - strcpy(RFR.RDB$FIELD_SOURCE, fieldSource.c_str()); - strcpy(RFR.RDB$RELATION_NAME, name.c_str()); - RFR.RDB$SYSTEM_FLAG = 0; + fieldDefinition.fieldSource = domainName->str_data; + } + else + { + string computedSource; + BlrWriter::BlrData computedValue; - RFR.RDB$NULL_FLAG.NULL = TRUE; - RFR.RDB$BASE_FIELD.NULL = TRUE; - RFR.RDB$UPDATE_FLAG.NULL = TRUE; - RFR.RDB$FIELD_POSITION.NULL = TRUE; - RFR.RDB$VIEW_CONTEXT.NULL = TRUE; - RFR.RDB$DEFAULT_VALUE.NULL = TRUE; - RFR.RDB$DEFAULT_SOURCE.NULL = TRUE; - RFR.RDB$COLLATION_ID.NULL = TRUE; - RFR.RDB$GENERATOR_NAME.NULL = TRUE; - RFR.RDB$IDENTITY_TYPE.NULL = TRUE; - - if (element->nod_arg[Dsql::e_dfl_collate]) + if (element->nod_arg[e_dfl_computed]) { - DDL_resolve_intl_type(dsqlScratch, field, (dsql_str*) element->nod_arg[Dsql::e_dfl_collate]); + field->fld_flags |= FLD_computed; - RFR.RDB$COLLATION_ID.NULL = FALSE; - RFR.RDB$COLLATION_ID = SSHORT(field->fld_collation_id); + defineComputed(tdbb, field, element->nod_arg[e_dfl_computed], + computedSource, computedValue); } - if (element->nod_arg[Dsql::e_dfl_identity]) - { - dsc desc; - MET_get_domain(tdbb, fieldSource, &desc, NULL); + TypeClause fieldType(field, NULL); // Don't use the collate in the generated domain. + fieldType.resolve(dsqlScratch); - if (!desc.isExact() || desc.dsc_scale != 0) - { - // Identity column @1 of table @2 must be exact numeric with zero scale. - status_exception::raise( - Arg::Gds(ENCODE_ISC_MSG(273, DYN_MSG_FAC)) << field->fld_name << name); - } + // Generate a domain. + storeGlobalField(tdbb, transaction, fieldDefinition.fieldSource, fieldType, + computedSource, computedValue); + } - MetaName sequenceName; - DYN_UTIL_generate_generator_name(tdbb, sequenceName); + if ((relation->rel_flags & REL_external) && + (field->fld_dtype == dtype_blob || field->fld_dtype == dtype_array || + field->fld_dimensions)) + { + const char* typeName = (field->fld_dtype == dtype_blob ? "BLOB" : "ARRAY"); - CreateSequenceNode::store(tdbb, transaction, sequenceName, - fb_sysflag_identity_generator); + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_dsql_type_not_supp_ext_tab) << typeName << name << field->fld_name); + } - RFR.RDB$GENERATOR_NAME.NULL = FALSE; - strcpy(RFR.RDB$GENERATOR_NAME, sequenceName.c_str()); + if (element->nod_arg[Dsql::e_dfl_collate]) + DDL_resolve_intl_type(dsqlScratch, field, (dsql_str*) element->nod_arg[Dsql::e_dfl_collate]); - RFR.RDB$IDENTITY_TYPE.NULL = FALSE; - RFR.RDB$IDENTITY_TYPE = IDENT_TYPE_BY_DEFAULT; - } + if (element->nod_arg[Dsql::e_dfl_identity]) + { + dsc desc; + MET_get_domain(tdbb, fieldDefinition.fieldSource, &desc, NULL); - if (notNullFlag) + if (!desc.isExact() || desc.dsc_scale != 0) { - RFR.RDB$NULL_FLAG.NULL = FALSE; - RFR.RDB$NULL_FLAG = TRUE; + // Identity column @1 of table @2 must be exact numeric with zero scale. + status_exception::raise( + Arg::Gds(ENCODE_ISC_MSG(273, DYN_MSG_FAC)) << field->fld_name << name); } - if (element->nod_arg[Dsql::e_dfl_default]) - { - string defaultSource; - BlrWriter::BlrData defaultValue; + DYN_UTIL_generate_generator_name(tdbb, fieldDefinition.identitySequence); - if (defineDefault(tdbb, field, element->nod_arg[Dsql::e_dfl_default], defaultSource, - defaultValue) && - notNullFlag) - { - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-204) << - Arg::Gds(isc_bad_default_value) << - Arg::Gds(isc_invalid_clause) << "default null not null"); - } + CreateSequenceNode::store(tdbb, transaction, fieldDefinition.identitySequence, + fb_sysflag_identity_generator); + } - RFR.RDB$DEFAULT_SOURCE.NULL = FALSE; - attachment->storeMetaDataBlob(tdbb, transaction, &RFR.RDB$DEFAULT_SOURCE, defaultSource); + BlrWriter::BlrData defaultValue; - RFR.RDB$DEFAULT_VALUE.NULL = FALSE; - attachment->storeBinaryBlob(tdbb, transaction, &RFR.RDB$DEFAULT_VALUE, defaultValue); - } - - if (position == -1) + if (element->nod_arg[Dsql::e_dfl_default]) + { + if (defineDefault(tdbb, field, element->nod_arg[Dsql::e_dfl_default], + fieldDefinition.defaultSource, defaultValue) && + notNullFlag) { - SLONG fieldPos = -1; - DYN_UTIL_generate_field_position(tdbb, NULL, name, &fieldPos); - if (fieldPos >= 0) - position = SSHORT(fieldPos + 1); + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-204) << + Arg::Gds(isc_bad_default_value) << + Arg::Gds(isc_invalid_clause) << "default null not null"); } - - if (position >= 0) - { - RFR.RDB$FIELD_POSITION.NULL = FALSE; - RFR.RDB$FIELD_POSITION = position; - } } - END_STORE + fieldDefinition.defaultValue = defaultValue; + + if (element->nod_arg[Dsql::e_dfl_collate]) + fieldDefinition.collationId = field->fld_collation_id; + + fieldDefinition.store(tdbb, transaction); + // Define the field constraints. for (ObjectsArray<Constraint>::iterator constraint(constraints.begin()); constraint != constraints.end(); @@ -5130,7 +5299,7 @@ if (field->fld_dtype <= dtype_any_text) { field->fld_character_set_id = DSC_GET_CHARSET(&desc); - field->fld_collation_id= DSC_GET_COLLATE(&desc); + field->fld_collation_id = DSC_GET_COLLATE(&desc); } else field->fld_sub_type = desc.dsc_sub_type; @@ -5639,9 +5808,7 @@ void RelationNode::defineCheckConstraintTrigger(Constraint& constraint, dsql_nod* node, FB_UINT64 triggerType) { - // See hack at pass1_field. - AutoSetRestore2<dsql_nod*, DsqlCompiledStatement> autoDdlNode(dsqlScratch->getStatement(), - &DsqlCompiledStatement::getDdlNode, &DsqlCompiledStatement::setDdlNode, node); + AutoSetRestore<bool> autoCheckConstraintTrigger(&dsqlScratch->checkConstraintTrigger, true); Constraint::BlrWriter& blrWriter = constraint.blrWritersHolder.add(); blrWriter.init(dsqlScratch); @@ -6085,7 +6252,7 @@ { Attachment* attachment = transaction->tra_attachment; - saveRelation(tdbb, dsqlScratch, MAKE_cstring(name.c_str()), true); + saveRelation(tdbb, dsqlScratch, name, false, true); if (externalFile) { @@ -6145,24 +6312,8 @@ } END_STORE - for (const TEXT* p = ALL_PRIVILEGES; *p; ++p) - { - request.reset(tdbb, drq_s_usr_prvs, DYN_REQUESTS); + storePrivileges(tdbb, transaction); - STORE(REQUEST_HANDLE request 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_relation; - X.RDB$PRIVILEGE[0] = *p; - X.RDB$PRIVILEGE[1] = 0; - X.RDB$GRANT_OPTION = 1; - } - END_STORE - } - ObjectsArray<Constraint> constraints; const dsql_nod* pkCols = findPkColumns(); SSHORT position = 0; @@ -6241,7 +6392,7 @@ { Attachment* attachment = transaction->tra_attachment; - saveRelation(tdbb, dsqlScratch, MAKE_cstring(name.c_str()), false); + saveRelation(tdbb, dsqlScratch, name, false, false); if (!dsqlScratch->relation) { @@ -7076,8 +7227,7 @@ void DropRelationNode::execute(thread_db* tdbb, jrd_tra* transaction) { - AutoPtr<dsql_str> nameStr(MAKE_string(name.c_str(), name.length())); - const dsql_rel* relation = METD_get_relation(transaction, dsqlScratch, nameStr); + const dsql_rel* relation = METD_get_relation(transaction, dsqlScratch, name); if (!relation && silent) return; @@ -7141,6 +7291,927 @@ //---------------------- +void CreateAlterViewNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const +{ + text.printf( + "CreateAlterViewNode\n" + " name: '%s'\n", + name.c_str()); +} + +void CreateAlterViewNode::execute(thread_db* tdbb, jrd_tra* transaction) +{ + Attachment* attachment = transaction->tra_attachment; + + const dsql_rel* modifyingView = NULL; + + if (alter) + { + modifyingView = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, name); + + if (!modifyingView && !create) + status_exception::raise(Arg::Gds(isc_dyn_view_not_found) << name); + } + + saveRelation(tdbb, dsqlScratch, name, true, modifyingView == NULL); + + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + + const int ddlTriggerAction = (modifyingView ? DDL_TRIGGER_ALTER_VIEW : DDL_TRIGGER_CREATE_VIEW); + + executeDdlTrigger(tdbb, transaction, DTW_BEFORE, ddlTriggerAction, name); + + if (!modifyingView) + DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_relation); + + // Compile the SELECT statement into a record selection expression, making sure to bump the + // context number since view contexts start at 1 (except for computed fields) -- note that + // calling PASS1_rse directly rather than PASS1_statement saves the context stack. + + DDL_reset_context_stack(dsqlScratch); + ++dsqlScratch->contextNumber; + selectExpr->nod_flags |= NOD_SELECT_VIEW_FIELDS; + dsql_nod* rse = PASS1_rse(dsqlScratch, selectExpr, NULL); + + dsqlScratch->getBlrData().clear(); + dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); + + // ASF: Call GEN_hidden_variables could be a optimization for views to not have + // blr_dcl_variables inside RSE loops, but this is currently not possible because it will + // mix the variables from view fields and view body. + + GEN_expr(dsqlScratch, rse); + dsqlScratch->appendUChar(blr_eoc); + + // Store the blr and source string for the view definition. + + if (modifyingView) + { + AutoCacheRequest request(tdbb, drq_m_view, DYN_REQUESTS); + bool found = false; + + FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + REL IN RDB$RELATIONS + WITH REL.RDB$RELATION_NAME EQ name.c_str() AND + REL.RDB$VIEW_BLR NOT MISSING + { + found = true; + + MODIFY REL + attachment->storeMetaDataBlob(tdbb, transaction, &REL.RDB$VIEW_SOURCE, source); + attachment->storeBinaryBlob(tdbb, transaction, &REL.RDB$VIEW_BLR, + dsqlScratch->getBlrData()); + END_MODIFY + } + END_FOR + + if (!found) + status_exception::raise(Arg::Gds(isc_dyn_view_not_found) << name); + + AutoRequest request2; + + FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) + VR IN RDB$VIEW_RELATIONS + WITH VR.RDB$VIEW_NAME EQ name.c_str() + { + ERASE VR; + } + END_FOR + + request2.reset(); + + FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) + TRG IN RDB$TRIGGERS + WITH TRG.RDB$RELATION_NAME EQ name.c_str() AND + TRG.RDB$SYSTEM_FLAG EQ fb_sysflag_view_check + { + ERASE TRG; + } + END_FOR + } + else + { + AutoCacheRequest request(tdbb, drq_s_rels, DYN_REQUESTS); + + STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + REL IN RDB$RELATIONS + { + strcpy(REL.RDB$RELATION_NAME, name.c_str()); + REL.RDB$SYSTEM_FLAG = 0; + REL.RDB$FLAGS = REL_sql; + REL.RDB$RELATION_TYPE = SSHORT(rel_view); + + attachment->storeMetaDataBlob(tdbb, transaction, &REL.RDB$VIEW_SOURCE, source); + attachment->storeBinaryBlob(tdbb, transaction, &REL.RDB$VIEW_BLR, dsqlScratch->getBlrData()); + } + END_STORE + + storePrivileges(tdbb, transaction); + } + + // Define the view source relations from the statement contexts and union contexts. + + while (dsqlScratch->derivedContext.hasData()) + dsqlScratch->context->push(dsqlScratch->derivedContext.pop()); + + while (dsqlScratch->unionContext.hasData()) + dsqlScratch->context->push(dsqlScratch->unionContext.pop()); + + AutoCacheRequest request(tdbb, drq_s_view_rels, DYN_REQUESTS); + + for (DsqlContextStack::iterator temp(*dsqlScratch->context); temp.hasData(); ++temp) + { + const dsql_ctx* context = temp.object(); + const dsql_rel* relation = context->ctx_relation; + const dsql_prc* procedure = context->ctx_procedure; + + if (relation || procedure) + { + const MetaName& refName = relation ? relation->rel_name : procedure->prc_name.identifier; + const char* contextName = context->ctx_alias ? context->ctx_alias : refName.c_str(); + + ViewContextType ctxType; + if (relation) + { + if (!(relation->rel_flags & REL_view)) + ctxType = VCT_TABLE; + else + ctxType = VCT_VIEW; + } + else //if (procedure) + ctxType = VCT_PROCEDURE; + + STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + VRL IN RDB$VIEW_RELATIONS + { + strcpy(VRL.RDB$VIEW_NAME, name.c_str()); + strcpy(VRL.RDB$RELATION_NAME, refName.c_str()); + VRL.RDB$CONTEXT_TYPE = SSHORT(ctxType); + VRL.RDB$VIEW_CONTEXT = context->ctx_context; + strcpy(VRL.RDB$CONTEXT_NAME, contextName); + + if (procedure && procedure->prc_name.package.hasData()) + { + VRL.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(VRL.RDB$PACKAGE_NAME, procedure->prc_name.package.c_str()); + } + else + VRL.RDB$PACKAGE_NAME.NULL = TRUE; + } + END_STORE + } + } + + // Check privileges on base tables and views. + + request.reset(tdbb, drq_l_view_rels, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + VRL IN RDB$VIEW_RELATIONS CROSS + PREL IN RDB$RELATIONS OVER RDB$RELATION_NAME + WITH VRL.RDB$PACKAGE_NAME MISSING AND + VRL.RDB$VIEW_NAME EQ name.c_str() + { + // CVC: This never matches so it causes unnecessary calls to verify, + // so I included a call to strip trailing blanks. + fb_utils::exact_name_limit(PREL.RDB$OWNER_NAME, sizeof(PREL.RDB$OWNER_NAME)); + + if (attachment->att_user->usr_user_name != PREL.RDB$OWNER_NAME) + { + SecurityClass::flags_t priv; + + // I think this should be the responsability of DFW or the user will find ways to + // circumvent DYN. + if (!DYN_UTIL_get_prot(tdbb, transaction, PREL.RDB$RELATION_NAME, "", &priv)) + { + // ASF: DYN_UTIL_get_prot will throw instead of return false. + fb_assert(false); + } + + if (!(priv & SCL_read)) + { + // msg 32: no permission for %s access to %s %s + status_exception::raise( + Arg::Gds(isc_no_priv) << Arg::Str("SELECT") << // Non-Translatable + // Remember, a view may be based on a view. + "TABLE/VIEW" << // Non-Translatable + // We want to print the name of the base table or view. + MetaName(PREL.RDB$RELATION_NAME)); + } + } + } + END_FOR + + // If there are field names defined for the view, match them in order with the items from the + // SELECT. Otherwise use all the fields from the rse node that was created from the select + // expression. + + const dsql_nod* const* ptr = NULL; + const dsql_nod* const* end = NULL; + + if (viewFields) + { + ptr = viewFields->nod_arg; + end = ptr + viewFields->nod_count; + } + + // Go through the fields list, defining or modifying the local fields; + // If an expression is specified rather than a field, define a global + // field for the computed value as well. + + dsql_nod* items = rse->nod_arg[Dsql::e_rse_items]; + dsql_nod** itemsPtr = items->nod_arg; + SortedArray<dsql_fld*> modifiedFields; + bool updatable = true; + SSHORT position = 0; + + for (const dsql_nod* const* const itemsEnd = itemsPtr + items->nod_count; + itemsPtr < itemsEnd; ++itemsPtr, ++position) + { + dsql_nod* fieldNode = *itemsPtr; + + // Determine the proper field name, replacing the default if necessary. + + const dsql_nod* nameNode = fieldNode; + const dsql_str* aliasName = NULL; + + while (nameNode->nod_type == Dsql::nod_alias || + nameNode->nod_type == Dsql::nod_derived_field || + nameNode->nod_type == Dsql::nod_map) + { + switch (nameNode->nod_type) + { + case Dsql::nod_alias: + if (!aliasName) + aliasName = (dsql_str*) nameNode->nod_arg[Dsql::e_alias_alias]; + nameNode = nameNode->nod_arg[Dsql::e_alias_value]; + break; + + case Dsql::nod_derived_field: + if (!aliasName) + aliasName = (dsql_str*) nameNode->nod_arg[Dsql::e_derived_field_name]; + nameNode = nameNode->nod_arg[Dsql::e_derived_field_value]; + break; + + case Dsql::nod_map: + { + const dsql_map* map = (dsql_map*) nameNode->nod_arg[Dsql::e_map_map]; + nameNode = map->map_node; + break; + } + + default: + break; + } + } + + const dsql_fld* nameField = NULL; + + if (nameNode->nod_type == Dsql::nod_field) + nameField = (dsql_fld*) nameNode->nod_arg[Dsql::e_fld_field]; + + const TEXT* fieldStr = NULL; + + if (aliasName) + fieldStr = aliasName->str_data; + else if (nameField) + fieldStr = nameField->fld_name.c_str(); + + // Check if this is a field or an expression. + + if (fieldNode->nod_type == Dsql::nod_alias) + fieldNode = fieldNode->nod_arg[Dsql::e_alias_value]; + + dsql_fld* field = NULL; + const dsql_ctx* context = NULL; + + if (fieldNode->nod_type == Dsql::nod_field) + { + field = (dsql_fld*) fieldNode->nod_arg[Dsql::e_fld_field]; + context = (dsql_ctx*) fieldNode->nod_arg[Dsql::e_fld_context]; + } + else + updatable = false; + + // If this is an expression, check to make sure there is a name specified. + + if (!ptr && !fieldStr) + { + // must specify field name for view select expression + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_specify_field_err)); + } + + // CVC: Small modification here to catch any mismatch between number of + // explicit field names in a view and number of fields in the select expression, + // see comment below. This closes Firebird Bug #223059. + if (ptr) + { + if (ptr < end) + { + const dsql_str* fieldName = (dsql_str*) (*ptr)->nod_arg[1]; + fieldStr = fieldName->str_data; + } + + ++ptr; + } + + // If not an expression, point to the proper base relation field, + // else make up an SQL field with generated global field for calculations. + + dsql_fld* relField = NULL; + + if (modifyingView) // if we're modifying a view + { + for (relField = modifyingView->rel_fields; relField; relField = relField->fld_next) + { + if (relField->fld_name == fieldStr) + { + if (modifiedFields.exist(relField)) + { + // column @1 appears more than once in ALTER VIEW + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-104) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_dsql_col_more_than_once_view) << Arg::Str(fieldStr)); + } + + modifiedFields.add(relField); + break; + } + } + } + + FieldDefinition fieldDefinition(*tdbb->getDefaultPool()); + fieldDefinition.relationName = name; + fieldDefinition.name = fieldStr; + fieldDefinition.position = position; + + // CVC: Not sure if something should be done now that isc_dyn_view_context is used here, + // but if alter view is going to work, maybe we need here the context type and package, too. + if (field) + { + TypeClause fieldType(field, NULL); + fieldType.resolve(dsqlScratch); + + fieldDefinition.viewContext = context->ctx_context; + fieldDefinition.baseField = field->fld_name; + + if (field->fld_dtype <= dtype_any_text) + fieldDefinition.collationId = field->fld_collation_id; + + if (relField) // modifying a view + { + // We're now modifying a field and it will be based on another one. So if the old + // field was an expression, delete it now. + + AutoRequest request2; + + FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) + RFL IN RDB$RELATION_FIELDS CROSS + FLD IN RDB$FIELDS + WITH RFL.RDB$FIELD_NAME EQ fieldStr AND + RFL.RDB$RELATION_NAME EQ name.c_str() AND + RFL.RDB$BASE_FIELD MISSING AND + FLD.RDB$FIELD_NAME EQ RFL.RDB$FIELD_SOURCE + { + bool wasInternalDomain = fb_utils::implicit_domain(FLD.RDB$FIELD_NAME); + fb_assert(wasInternalDomain); + + if (wasInternalDomain) + ERASE FLD; + } + END_FOR + + fieldDefinition.modify(tdbb, transaction); + } + else + fieldDefinition.store(tdbb, transaction); + } + else + { + dsqlScratch->getBlrData().clear(); + dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); + GEN_expr(dsqlScratch, fieldNode); + dsqlScratch->appendUChar(blr_eoc); + + // Get the type of the expression. + dsc desc; + MAKE_desc(dsqlScratch, &desc, fieldNode, NULL); + + dsql_fld newField(*tdbb->getDefaultPool()); + newField.fld_dtype = desc.dsc_dtype; + newField.fld_length = desc.dsc_length; + newField.fld_scale = desc.dsc_scale; + + if (desc.isText() || (desc.isBlob() && desc.getBlobSubType() == isc_blob_text)) + { + newField.fld_character_set_id = desc.getCharSet(); + newField.fld_collation_id = desc.getTextType(); + } + + if (desc.isText()) + { + newField.fld_character_length = newField.fld_length; + newField.fld_length *= METD_get_charset_bpc( + dsqlScratch->getTransaction(), newField.fld_character_set_id); + } + else + newField.fld_sub_type = desc.dsc_sub_type; + + TypeClause fieldType(&newField, NULL); + fieldType.setup(dsqlScratch); + + if (relField) // modifying a view + { + AutoRequest request2; + + FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) + RFL IN RDB$RELATION_FIELDS CROSS + FLD IN RDB$FIELDS + WITH RFL.RDB$FIELD_NAME EQ fieldStr AND + RFL.RDB$RELATION_NAME EQ name.c_str() AND + RFL.RDB$BASE_FIELD MISSING AND + FLD.RDB$FIELD_NAME EQ RFL.RDB$FIELD_SOURCE + { + bool wasInternalDomain = fb_utils::implicit_domain(FLD.RDB$FIELD_NAME); + fb_assert(wasInternalDomain); + + if (wasInternalDomain) + { + fieldDefinition.fieldSource = FLD.RDB$FIELD_NAME; + + MODIFY FLD + updateRdbFields(fieldType, + 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); + + FLD.RDB$COMPUTED_BLR.NULL = FALSE; + attachment->storeBinaryBlob(tdbb, transaction, &FLD.RDB$COMPUTED_BLR, + dsqlScratch->getBlrData()); + END_MODIFY + } + } + END_FOR + + if (fieldDefinition.fieldSource.isEmpty()) + { + storeGlobalField(tdbb, transaction, fieldDefinition.fieldSource, fieldType, + "", dsqlScratch->getBlrData()); + } + + fieldDefinition.modify(tdbb, transaction); + } + else + { + storeGlobalField(tdbb, transaction, fieldDefinition.fieldSource, fieldType, + "", dsqlScratch->getBlrData()); + + fieldDefinition.store(tdbb, transaction); + } + } + + if (fieldStr) + saveField(tdbb, dsqlScratch, fieldStr); + } + + // CVC: This message was not catching the case when + // #fields < items in select list, see comment above. + + if (ptr != end) + { + // number of fields does not match select list + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_num_field_err)); + } + + if (modifyingView) // modifying a view + { + // Delete the old fields not present in the new definition. + for (dsql_fld* relField = modifyingView->rel_fields; relField; relField = relField->fld_next) + { + if (!modifiedFields.exist(relField)) + deleteLocalField(tdbb, transaction, name, relField->fld_name); + } + } + + // Setup to define triggers for WITH CHECK OPTION. + + if (withCheckOption) + { + if (!updatable) + { + // Only simple column names permitted for VIEW WITH CHECK OPTION + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_col_name_err)); + } + + dsql_nod* querySpec = selectExpr->nod_arg[Dsql::e_sel_query_spec]; + + if (querySpec->nod_type == Dsql::nod_list) + { + // Only one table allowed for VIEW WITH CHECK OPTION + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_table_view_err)); + } + + if (querySpec->nod_arg[Dsql::e_qry_from]->nod_count != 1) + { + // Only one table allowed for VIEW WITH CHECK OPTION + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_table_view_err)); + } + + if (!querySpec->nod_arg[Dsql::e_qry_where]) + { + // No where clause for VIEW WITH CHECK OPTION + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_where_err)); + } + + if (querySpec->nod_arg[Dsql::e_qry_distinct] || querySpec->nod_arg[Dsql::e_qry_group] || + querySpec->nod_arg[Dsql::e_qry_having]) + { + // DISTINCT, GROUP or HAVING not permitted for VIEW WITH CHECK OPTION + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_distinct_err)); + } + + createCheckTriggers(tdbb, transaction, items); + } + + DDL_reset_context_stack(dsqlScratch); + + executeDdlTrigger(tdbb, transaction, DTW_AFTER, ddlTriggerAction, name); + + savePoint.release(); // everything is ok + + // Update DSQL cache + METD_drop_relation(transaction, name); + MET_dsql_cache_release(tdbb, SYM_relation, name); +} + +// Generate triggers to implement the WITH CHECK OPTION clause for a VIEW. +void CreateAlterViewNode::createCheckTriggers(thread_db* tdbb, jrd_tra* transaction, dsql_nod* items) +{ + // Specify that the trigger should abort if the condition is not met. + dsql_nod* actionNode = MAKE_node(Dsql::nod_list, 1); + actionNode->nod_arg[0] = MAKE_node(Dsql::nod_gdscode, 1); + actionNode->nod_arg[0]->nod_arg[0] = (dsql_nod*) MAKE_cstring("check_constraint"); + + // Create the UPDATE trigger. + + dsql_nod* baseAndNode = NULL; + dsql_nod* baseRelation = NULL; + defineUpdateAction(dsqlScratch, &baseAndNode, &baseRelation, items); + fb_assert(baseAndNode); + fb_assert(baseRelation); + + dsql_nod* rse = MAKE_node(Dsql::nod_rse, e_rse_count); + rse->nod_arg[e_rse_boolean] = baseAndNode; + dsql_nod* temp = MAKE_node(Dsql::nod_list, 1); + rse->nod_arg[e_rse_streams] = temp; + temp->nod_arg[0] = baseRelation; + + createCheckTrigger(tdbb, dsqlScratch, rse, items, actionNode, PRE_MODIFY_TRIGGER); + createCheckTrigger(tdbb, dsqlScratch, NULL, items, actionNode, PRE_STORE_TRIGGER); +} + +// Define a trigger for a VIEW WITH CHECK OPTION. +void CreateAlterViewNode::createCheckTrigger(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, + dsql_nod* rse, dsql_nod* items, dsql_nod* actions, TriggerType triggerType) +{ + AutoSetRestore<bool> autoCheckConstraintTrigger(&dsqlScratch->checkConstraintTrigger, true); + + const dsql_nod* querySpec = selectExpr->nod_arg[Dsql::e_sel_query_spec]; + dsql_nod* relationNode = dsqlNode; + + // Generate the trigger blr. + + dsqlScratch->getBlrData().clear(); + dsqlScratch->getDebugData().clear(); + dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); + + dsqlScratch->appendUChar(blr_begin); + + // Create the "OLD" and "NEW" contexts for the trigger -- the new one could be a dummy place + // holder to avoid resolving fields to that context but prevent relations referenced in + // the trigger actions from referencing the predefined "1" context. + + dsql_ctx* savContext = NULL; + dsql_ctx* context = NULL; + + if (dsqlScratch->contextNumber) + { + // If an alias is specified for the single base table involved, + // save and then add the context. + + context = dsqlScratch->context->object(); + + if (context->ctx_alias) + { + MemoryPool& pool = *tdbb->getDefaultPool(); + savContext = FB_NEW(pool) dsql_ctx(pool); + *savContext = *context; + } + } + + DDL_reset_context_stack(dsqlScratch); + + dsql_nod* tempAlias = relationNode->nod_arg[Dsql::e_rln_alias]; + + relationNode->nod_arg[Dsql::e_rln_alias] = (dsql_nod*) MAKE_cstring(OLD_CONTEXT); + dsql_ctx* oldContext = PASS1_make_context(dsqlScratch, relationNode); + oldContext->ctx_flags |= CTX_system; + + relationNode->nod_arg[Dsql::e_rln_alias] = (dsql_nod*) MAKE_cstring(NEW_CONTEXT); + dsql_ctx* newContext = PASS1_make_context(dsqlScratch, relationNode); + newContext->ctx_flags |= CTX_system; + + relationNode->nod_arg[Dsql::e_rln_alias] = tempAlias; + + if (savContext) + { + savContext->ctx_context = dsqlScratch->contextNumber++; + context->ctx_scope_level = dsqlScratch->scopeLevel; + dsqlScratch->context->push(savContext); + } + + // Generate the condition for firing the trigger. + + dsql_nod* condition; + + if (triggerType == PRE_MODIFY_TRIGGER) + { + dsqlScratch->appendUChar(blr_for); + + dsql_nod* temp = rse->nod_arg[Dsql::e_rse_streams]; + temp->nod_arg[0] = PASS1_node(dsqlScratch, temp->nod_arg[0]); + temp = rse->nod_arg[Dsql::e_rse_boolean]; + + rse->nod_arg[Dsql::e_rse_boolean] = PASS1_node(dsqlScratch, temp); + GEN_expr(dsqlScratch, rse); + + condition = replaceFieldNames(querySpec->nod_arg[Dsql::e_qry_where], items, + viewFields, false, NEW_CONTEXT); + } + else if (triggerType == PRE_STORE_TRIGGER) + { + condition = replaceFieldNames(querySpec->nod_arg[Dsql::e_qry_where], items, + viewFields, true, NEW_CONTEXT); + } + else + { + fb_assert(false); + } + + dsqlScratch->appendUChar(blr_if); + GEN_expr(dsqlScratch, PASS1_node(dsqlScratch, condition)); + dsqlScratch->appendUChar(blr_begin); + dsqlScratch->appendUChar(blr_end); + + // Generate the action statements for the trigger. + + dsql_nod** ptr = actions->nod_arg; + + for (const dsql_nod* const* const end = ptr + actions->nod_count; ptr != end; ++ptr) + GEN_statement(dsqlScratch, PASS1_statement(dsqlScratch, *ptr)); + + dsqlScratch->appendUChar(blr_end); // of begin + dsqlScratch->appendUChar(blr_eoc); + + DDL_reset_context_stack(dsqlScratch); + + TriggerDefinition trigger(*tdbb->getDefaultPool()); + trigger.systemFlag = fb_sysflag_view_check; + trigger.relationName = name; + trigger.type = triggerType; + trigger.blrData = dsqlScratch->getBlrData(); + trigger.store(tdbb, dsqlScratch->getTransaction()); +} + +// Define an action statement which, given a view definition, will map an update to a record from +// a view of a single relation into the base relation. +void CreateAlterViewNode::defineUpdateAction(DsqlCompilerScratch* dsqlScratch, + dsql_nod** baseAndNode, dsql_nod** baseRelation, dsql_nod* items) +{ + // Check whether this is an updatable view definition. + + dsql_nod* querySpec = NULL; + dsql_nod* fromList = NULL; + + if (!(querySpec = selectExpr->nod_arg[Dsql::e_sel_query_spec]) || + !(fromList = querySpec->nod_arg[Dsql::e_qry_from]) || + fromList->nod_count != 1) + { + // The caller seems throwing proper errors for all the above conditions. + // But just in case it doesn't, here we have the final attempt to prevent the bad things. + fb_assert(false); + } + + // Use the relation referenced in the select statement for rse. + + dsql_nod* relationNode = MAKE_node(Dsql::nod_relation_name, (int) Dsql::e_rln_count); + relationNode->nod_arg[Dsql::e_rln_name] = fromList->nod_arg[0]->nod_arg[Dsql::e_rln_name]; + relationNode->nod_arg[Dsql::e_rln_alias] = (dsql_nod*) MAKE_cstring(TEMP_CONTEXT); + *baseRelation = relationNode; + + // Get the list of values and fields to compare to -- if there is no list of fields, get all + // fields in the base relation that are not computed. + + dsql_nod* valuesNode = viewFields; + dsql_nod* fieldsNode = querySpec->nod_arg[Dsql::e_qry_list]; + + if (!fieldsNode) + { + const dsql_rel* relation = METD_get_relation(dsqlScratch->getTransaction(), + dsqlScratch, name); + DsqlNodStack field_stack; + + for (const dsql_fld* field = relation->rel_fields; field; field = field->fld_next) + { + if (!(field->fld_flags & FLD_computed)) + field_stack.push(MAKE_field_name(field->fld_name.c_str())); + } + + fieldsNode = MAKE_list(field_stack); + } + + if (!valuesNode) + valuesNode = fieldsNode; + + // Generate the list of assignments to fields in the base relation. + + dsql_nod** ptr = fieldsNode->nod_arg; + const dsql_nod* const* const end = ptr + fieldsNode->nod_count; + dsql_nod** ptr2 = valuesNode->nod_arg; + const dsql_nod* const* const end2 = ptr2 + valuesNode->nod_count; + dsql_nod* andNode = MAKE_node(Dsql::nod_and, 2); + int andArg = 0; + + for (; (ptr < end) && (ptr2 < end2); ptr++, ptr2++) + { + dsql_nod* fieldNode = *ptr; + if (fieldNode->nod_type == Dsql::nod_alias) + fieldNode = fieldNode->nod_arg[Dsql::e_alias_value]; + + // Generate the actual comparisons. + + if (fieldNode->nod_type == Dsql::nod_field_name) + { + fieldNode->nod_arg[Dsql::e_fln_context] = (dsql_nod*) MAKE_cstring(TEMP_CONTEXT); + + dsql_nod* oldValueNode = MAKE_node(Dsql::nod_field_name, (int) Dsql::e_fln_count); + oldValueNode->nod_arg[Dsql::e_fln_name] = (*ptr2)->nod_arg[Dsql::e_fln_name]; + oldValueNode->nod_arg[Dsql::e_fln_context] = (dsql_nod*) MAKE_cstring(OLD_CONTEXT); + + dsql_nod* eqlNode = MAKE_node(Dsql::nod_eql, 2); + eqlNode->nod_arg[0] = oldValueNode; + eqlNode->nod_arg[1] = fieldNode; + + dsql_nod* nullNode1 = MAKE_node(Dsql::nod_missing, 1); + nullNode1->nod_arg[0] = oldValueNode; + dsql_nod* nullNode2 = MAKE_node(Dsql::nod_missing, 1); + nullNode2->nod_arg[0] = fieldNode; + + dsql_nod* andNode2 = MAKE_node(Dsql::nod_and, 2); + andNode2->nod_arg[0] = nullNode1; + andNode2->nod_arg[1] = nullNode2; + + dsql_nod* orNode = MAKE_node(Dsql::nod_or, 2); + orNode->nod_arg[0] = eqlNode; + orNode->nod_arg[1] = andNode2; + + if (andArg <= 1) + andNode->nod_arg[andArg++] = orNode; + else + { + dsql_nod* oldAnd = andNode; + andNode = MAKE_node(Dsql::nod_and, (int) 2); + andNode->nod_arg[0] = oldAnd; + andNode->nod_arg[1] = orNode; + } + } + } + + if (andArg <= 1) + { + andNode->nod_arg[andArg] = replaceFieldNames(querySpec->nod_arg[Dsql::e_qry_where], + items, NULL, false, TEMP_CONTEXT); + } + else + { + dsql_nod* oldAnd = andNode; + andNode = MAKE_node(Dsql::nod_and, 2); + andNode->nod_arg[0] = oldAnd; + andNode->nod_arg[1] = replaceFieldNames(querySpec->nod_arg[Dsql::e_qry_where], + items, NULL, false, TEMP_CONTEXT); + } + + *baseAndNode = andNode; +} + +// Given an input node tree, find any field name nodes and replace them according to the mapping +// provided. This is used to create view WITH CHECK OPTION. +dsql_nod* CreateAlterViewNode::replaceFieldNames(dsql_nod* input, dsql_nod* searchFields, + dsql_nod* replaceFields, bool nullThem, const char* contextName) +{ + if (!input || input->getType() != dsql_type_nod) + return input; + + const dsql_nod* const* const endo = input->nod_arg + input->nod_count; + + for (dsql_nod** ptr = input->nod_arg; ptr < endo; ++ptr) + { + if ((*ptr)->nod_type == Dsql::nod_select_expr) + { + // No subqueries permitted for VIEW WITH CHECK OPTION + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_subquery_err)); + } + + if ((*ptr)->nod_type == Dsql::nod_field_name) + { + // Found a field node, check if it needs to be replaced. + + const dsql_str* fieldName = (dsql_str*) (*ptr)->nod_arg[Dsql::e_fln_name]; + dsql_nod** search = searchFields->nod_arg; + const dsql_nod* const* const end = search + searchFields->nod_count; + dsql_nod** replace = NULL; + + if (replaceFields) + replace = replaceFields->nod_arg; + + bool found = false; + + for (; search < end; ++search, replaceFields ? ++replace : NULL) + { + const dsql_str* replaceName = NULL; + if (replaceFields) + replaceName = (dsql_str*) (*replace)->nod_arg[Dsql::e_fln_name]; + + const dsql_nod* fieldNode = *search; + const dsql_fld* field = (dsql_fld*) fieldNode->nod_arg[Dsql::e_fld_field]; + + if (field->fld_name == fieldName->str_data) + { + found = true; + + if (replaceFields) + (*ptr)->nod_arg[e_fln_name] = (*replace)->nod_arg[Dsql::e_fln_name]; + + (*ptr)->nod_arg[Dsql::e_fln_context] = (dsql_nod*) MAKE_cstring(contextName); + + } + + if (nullThem && replaceFields && + strcmp(fieldName->str_data, replaceName->str_data) == 0) + { + found = true; + } + } + + if (nullThem && !found) + (*ptr) = MAKE_node(Dsql::nod_null, 0); + } + else + { + // Recursively go through the input tree looking for field name nodes. + replaceFieldNames(*ptr, searchFields, replaceFields, nullThem, contextName); + } + } + + return input; +} + + +//---------------------- + + // Store an index. void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, MetaName& name, Definition& definition, MetaName* referredIndexName) Modified: firebird/trunk/src/dsql/DdlNodes.h =================================================================== --- firebird/trunk/src/dsql/DdlNodes.h 2010-08-01 20:43:29 UTC (rev 51382) +++ firebird/trunk/src/dsql/DdlNodes.h 2010-08-02 02:22:26 UTC (rev 51383) @@ -68,6 +68,7 @@ public: void resolve(DsqlCompilerScratch* dsqlScratch, bool modifying = false); + void setup(DsqlCompilerScratch* dsqlScratch); public: virtual void print(Firebird::string& text) const; @@ -961,6 +962,36 @@ class RelationNode : public DdlNode { public: + class FieldDefinition + { + public: + FieldDefinition(MemoryPool& p) + : name(p), + relationName(p), + fieldSource(p), + identitySequence(p), + defaultSource(p), + baseField(p) + { + } + + void modify(thread_db* tdbb, jrd_tra* transaction); + void store(thread_db* tdbb, jrd_tra* transaction); + + public: + Firebird::MetaName name; + Firebird::MetaName relationName; + Firebird::MetaName fieldSource; + Firebird::MetaName identitySequence; + Nullable<USHORT> collationId; + Nullable<bool> notNullFlag; + Nullable<USHORT> position; + Firebird::string defaultSource; + Firebird::ByteChunk defaultValue; + Nullable<USHORT> viewContext; + Firebird::MetaName baseField; + }; + struct Constraint : public PermanentStorage { enum Type { TYPE_CHECK, TYPE_NOT_NULL, TYPE_PK, TYPE_UNIQUE, TYPE_FK }; @@ -1039,6 +1070,7 @@ const Firebird::MetaName& relationName, const Firebird::MetaName& fieldName); protected: + void storePrivileges(thread_db* tdbb, jrd_tra* transaction); void defineField(thread_db* tdbb, jrd_tra* transaction, const dsql_nod* element, SSHORT position, const dsql_nod* pkcols); void defineComputed(thread_db* tdbb, dsql_fld* field, dsql_nod* node, @@ -1159,10 +1191,10 @@ { public: explicit RecreateRelationNode(MemoryPool& p, const Firebird::string& sqlText, - CreateRelationNode* aCreateNode) + RelationNode* aCreateNode, bool view) : DdlNode(p, sqlText), createNode(aCreateNode), - dropNode(p, sqlText, createNode->name, false) + dropNode(p, sqlText, createNode->name, view) { dropNode.silent = true; } @@ -1174,17 +1206,67 @@ protected: virtual void putErrorPrefix(Firebird::Arg::StatusVector& statusVector) { - statusVector << Firebird::Arg::Gds(isc_dsql_recreate_table_failed) << createNode->name; + ISC_STATUS code = dropNode.view ? + isc_dsql_recreate_table_failed : isc_dsql_recreate_table_failed; + statusVector << Firebird::Arg::Gds(code) << createNode->name; } virtual DdlNode* internalDsqlPass(); private: - CreateRelationNode* createNode; + RelationNode* createNode; DropRelationNode dropNode; }; +class CreateAlterViewNode : public RelationNode +{ +public: + explicit CreateAlterViewNode(MemoryPool& p, const Firebird::string& sqlText, + dsql_nod* aDsqlNode, dsql_nod* aViewFields, dsql_nod* aSelectExpr) + : RelationNode(p, sqlText, aDsqlNode), + create(true), + alter(false), + viewFields(aViewFields), + selectExpr(aSelectExpr), + source(p), + withCheckOption(false) + { + } + +public: + virtual void print(Firebird::string& text, Firebird::Array<dsql_nod*>& nodes) const; + virtual void execute(thread_db* tdbb, jrd_tra* transaction); + +protected: + virtual void putErrorPrefix(Firebird::Arg::StatusVector& statusVector) + { + statusVector << + Firebird::Arg::Gds(createAlterCode(create, alter, + isc_dsql_create_view_failed, isc_dsql_alter_view_failed, + isc_dsql_create_alter_view_failed)) << + name; + } + +private: + void createCheckTriggers(thread_db* tdbb, jrd_tra* transaction, dsql_nod* items); + void createCheckTrigger(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, + dsql_nod* rse, dsql_nod* items, dsql_nod* actions, TriggerType triggerType); + void defineUpdateAction(DsqlCompilerScratch* dsqlScratch, dsql_nod** baseAndNode, + dsql_nod** baseRelation, dsql_nod* items); + static dsql_nod* replaceFieldNames(dsql_nod* input, dsql_nod* searchFields, + dsql_nod* replaceFields, bool nullThem, const char* contextName); + +public: + bool create; + bool alter; + dsql_nod* viewFields; + dsql_nod* selectExpr; + Firebird::string source; + bool withCheckOption; +}; + + class CreateIndexNode { public: Modified: firebird/trunk/src/dsql/ddl.cpp =================================================================== --- firebird/trunk/src/dsql/ddl.cpp 2010-08-01 20:43:29 UTC (rev 51382) +++ firebird/trunk/src/dsql/ddl.cpp 2010-08-02 02:22:26 UTC (rev 51383) @@ -113,7 +113,6 @@ static void assign_field_length(dsql_fld*, USHORT); -static void create_view_triggers(DsqlCompilerScratch*, dsql_nod*, dsql_nod*); static void define_computed(DsqlCompilerScratch*, dsql_nod*, dsql_fld*, dsql_nod*); static void define_database(DsqlCompilerScratch*); static void define_filter(DsqlCompilerScratch*); @@ -122,10 +121,6 @@ static void define_index(DsqlCompilerScratch*); static void define_shadow(DsqlCompilerScratch*); static void define_udf(DsqlCompilerScratch*); -static void define_update_action(DsqlCompilerScratch*, dsql_nod**, dsql_nod**, dsql_nod*); -static void define_view(DsqlCompilerScratch*, NOD_TYPE); -static void define_view_trigger(DsqlCompilerScratch*, dsql_nod*, dsql_nod*, dsql_nod*); -static void delete_relation_view(DsqlCompilerScratch*, dsql_nod*, bool); static void generate_dyn(DsqlCompilerScratch*, dsql_nod*); static void grant_revoke(DsqlCompilerScratch*); static void modify_database(DsqlCompilerScratch*); @@ -139,11 +134,8 @@ static void modify_udf(DsqlCompilerScratch*); static void modify_map(DsqlCompilerScratch*); static void process_role_nm_list(DsqlCompilerScratch*, SSHORT, const dsql_nod*, const dsql_nod*, NOD_TYPE, const dsql_nod*); -static void put_descriptor(DsqlCompilerScratch*, const dsc*); static void put_field(DsqlCompilerScratch*, dsql_fld*, bool); static dsql_nod* replace_field_names(dsql_nod*, dsql_nod*, dsql_nod*, bool, const char*); -static void save_field(DsqlCompilerScratch*, const SCHAR*); -static void save_relation(DsqlCompilerScratch*, const dsql_str*); static void set_statistics(DsqlCompilerScratch*); static void define_user(DsqlCompilerScratch*, UCHAR); static void put_grantor(DsqlCompilerScratch* dsqlScratch, const dsql_nod* grantor); @@ -189,14 +181,6 @@ switch (type) { - case nod_mod_view: - case nod_replace_view: - case nod_redef_view: - string = (dsql_str*) statement->getDdlNode()->nod_arg[e_alt_name]; - sym_type = SYM_relation; - METD_drop_relation(request->getTransaction(), string->str_data); - break; - case nod_del_udf: case nod_mod_udf: // Signal UDF for obsolescence @@ -329,7 +313,7 @@ if (field->fld_type_of_table) { dsql_rel* relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, - field->fld_type_of_table); + field->fld_type_of_table->str_data); const dsql_fld* fld = NULL; if (relation) @@ -633,61 +617,6 @@ } -static void create_view_triggers(DsqlCompilerScratch* dsqlScratch, dsql_nod* element, dsql_nod* items) -{ -/************************************** - * - * c r e a t e _ v i e w _ t r i g g e r s - * - ************************************** - * - * Function - * Generate triggers to implement the WITH CHECK OPTION - * clause for a VIEW - * - **************************************/ - - dsql_nod* ddl_node = dsqlScratch->getStatement()->getDdlNode(); - - if (!element->nod_arg[e_cnstr_table]) { - element->nod_arg[e_cnstr_table] = ddl_node->nod_arg[e_drl_name]; - } - - // specify that the trigger should abort if the condition is not met - - dsql_nod* list_node = MAKE_node(nod_list, 1); - element->nod_arg[e_cnstr_actions] = list_node; - list_node->nod_arg[0] = MAKE_node(nod_gdscode, 1); - - dsql_nod** errorcode_node = &lis... [truncated message content] |