/* * PROGRAM: JRD Data Definition Utility * MODULE: dyn_mod.epp * DESCRIPTION: Dynamic data definition - DYN_modify_ * * The contents of this file are subject to the Interbase Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy * of the License at http://www.Inprise.com/IPL.html * * Software distributed under the License is distributed on an * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express * or implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code was created by Inprise Corporation * and its predecessors. Portions created by Inprise Corporation are * Copyright (C) Inprise Corporation. * * All Rights Reserved. * Contributor(s): ______________________________________. * 2001.5.20: Claudio Valderrama: when changing a domain's name, * if it has dimensions, rdb$field_dimensions should be updated, too. * 2001.5.23: Claudio Valderrama: Forbid zero length identifiers, * they are not ANSI SQL compliant. * 2001.5.27 Claudio Valderrama: Prevent rdb$field_length from going * out of sync when toggling between char and varchar data types. * This caused check_update_fld_type() to lose ability to detect potentially * dangerous changes. For example, you could alter a field or a domain and * be able to change char(10) to varchar(8). * Unfortunately, Borland chose to have DYN_modify_global_field() and add to the * party DYN_modify_sql_field(); therefore bug fixes should be done twice. * 2001.10.08 Claudio Valderrama: put a comment with suggested code to hide * special non-system triggers from user manipulation. * 2002-02-24 Sean Leyne - Code Cleanup of old Win 3.1 port (WINDOWS_ONLY) * 2002.08.10 Dmitry Yemanov: ALTER VIEW */ #include "firebird.h" #include #include #include "../jrd/common.h" #include "../jrd/jrd.h" #include "../jrd/tra.h" #include "../jrd/scl.h" #include "../jrd/drq.h" #include "../jrd/flags.h" #include "../jrd/ibase.h" #include "../jrd/lls.h" #include "../jrd/met.h" #include "../jrd/btr.h" #include "../jrd/ini.h" #include "../jrd/intl.h" #include "../jrd/dyn.h" #include "../jrd/ods.h" #include "../jrd/blb_proto.h" #include "../jrd/cmp_proto.h" #include "../jrd/dyn_proto.h" #include "../jrd/dyn_df_proto.h" #include "../jrd/dyn_md_proto.h" #include "../jrd/dyn_ut_proto.h" #include "../jrd/err_proto.h" #include "../jrd/exe_proto.h" #include "../jrd/gds_proto.h" #include "../jrd/inf_proto.h" #include "../jrd/intl_proto.h" #include "../jrd/isc_f_proto.h" #include "../jrd/met_proto.h" #include "../jrd/vio_proto.h" #include "../jrd/dsc_proto.h" #include "../common/utils_proto.h" #include "../dsql/DdlNodes.h" #include "../dsql/metd_proto.h" using MsgFormat::SafeArg; using namespace Jrd; using namespace Firebird; DATABASE DB = STATIC "ODS.RDB"; static const UCHAR alloc_info[] = { isc_info_allocation, isc_info_end }; static void change_backup_mode(Global*, UCHAR verb); static void modify_lfield_position(thread_db*, Global*, const MetaName&, const MetaName&, USHORT, USHORT); static bool check_view_dependency(thread_db*, Global*, const MetaName&, const MetaName&); static bool check_sptrig_dependency(thread_db*, Global*, const MetaName&, const MetaName&); static void modify_lfield_index(thread_db*, Global*, const MetaName&, const MetaName&, const MetaName&); static bool field_exists(thread_db*, Global*, const MetaName&, const MetaName&); static bool domain_exists(thread_db*, Global*, const MetaName&); static void get_domain_type(thread_db*, Global*, dyn_fld&); static ULONG check_update_fld_type(const dyn_fld&, const dyn_fld&); static ULONG check_update_numeric_type(const dyn_fld&, const dyn_fld&); static void modify_err_punt(thread_db*, ULONG, const dyn_fld&, const dyn_fld&); void DYN_modify_database( Global* gbl, const UCHAR** ptr) { /************************************** * * D Y N _ m o d i f y _ d a t a b a s e * ************************************** * * Functional description * Modify a database. * **************************************/ UCHAR s[128]; thread_db* tdbb = JRD_get_thread_data(); Database* dbb = tdbb->getDatabase(); jrd_req* request = NULL; try { INF_database_info(alloc_info, sizeof(alloc_info), s, sizeof(s)); if (s[0] != isc_info_allocation) { goto dyn_punt_84; } request = CMP_find_request(tdbb, drq_m_database, DYN_REQUESTS); const SSHORT length = gds__vax_integer(s + 1, 2); SLONG start = gds__vax_integer(s + 3, length); FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) DBB IN RDB$DATABASE if (!DYN_REQUEST(drq_m_database)) DYN_REQUEST(drq_m_database) = request; MODIFY DBB USING UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) { switch (verb) { case isc_dyn_security_class: if (GET_STRING(ptr, DBB.RDB$SECURITY_CLASS)) DBB.RDB$SECURITY_CLASS.NULL = FALSE; else DBB.RDB$SECURITY_CLASS.NULL = TRUE; break; case isc_dyn_def_file: DYN_define_file(gbl, ptr, (SLONG) 0, &start, 84); break; case isc_dyn_def_difference: DYN_define_difference(gbl, ptr); break; case isc_dyn_drop_difference: case isc_dyn_begin_backup: case isc_dyn_end_backup: change_backup_mode(gbl, verb); break; case isc_dyn_fld_character_set_name: if (!tdbb->getAttachment()->locksmith()) ERR_post(Arg::Gds(isc_adm_task_denied)); if (GET_STRING(ptr, DBB.RDB$CHARACTER_SET_NAME)) DBB.RDB$CHARACTER_SET_NAME.NULL = FALSE; else DBB.RDB$CHARACTER_SET_NAME.NULL = TRUE; break; case isc_dyn_fld_collation: { MetaName collation; GET_STRING(ptr, collation); if (!DBB.RDB$CHARACTER_SET_NAME.NULL) { AlterCharSetNode node(*tdbb->getDefaultPool(), "", DBB.RDB$CHARACTER_SET_NAME, collation); node.execute(tdbb, gbl->gbl_transaction); } } break; default: --(*ptr); DYN_execute(gbl, ptr, NULL, NULL, NULL, NULL, NULL); } } END_MODIFY; END_FOR; if (!DYN_REQUEST(drq_m_database)) DYN_REQUEST(drq_m_database) = request; } catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); DYN_rundown_request(request, -1); DYN_error_punt(true, 84); // msg 84: "MODIFY DATABASE failed" } return; dyn_punt_84: DYN_error_punt(true, 84); // msg 84: "MODIFY DATABASE failed" } void DYN_modify_exception( Global* gbl, const UCHAR** ptr) { /************************************** * * D Y N _ m o d i f y _ e x c e p t i o n * ************************************** * * Functional description * Modify an exception. * **************************************/ thread_db* tdbb = JRD_get_thread_data(); Database* dbb = tdbb->getDatabase(); jrd_req* request = CMP_find_request(tdbb, drq_m_xcp, DYN_REQUESTS); bool found = false; MetaName t; GET_STRING(ptr, t); try { found = false; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) X IN RDB$EXCEPTIONS WITH X.RDB$EXCEPTION_NAME EQ t.c_str() if (!DYN_REQUEST(drq_m_xcp)) DYN_REQUEST(drq_m_xcp) = request; found = true; DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, DDL_TRIGGER_ALTER_EXCEPTION, t, gbl->sqlText); MODIFY X UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) switch (verb) { case isc_dyn_xcp_msg: GET_BYTES(ptr, X.RDB$MESSAGE); X.RDB$MESSAGE.NULL = FALSE; break; default: DYN_unsupported_verb(); } END_MODIFY; END_FOR; if (!DYN_REQUEST(drq_m_xcp)) DYN_REQUEST(drq_m_xcp) = request; } // try catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); DYN_rundown_request(request, -1); DYN_error_punt(true, 145); // msg 145: "MODIFY EXCEPTION failed" } if (found) { DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, DDL_TRIGGER_ALTER_EXCEPTION, t, gbl->sqlText); } else { DYN_error_punt(false, 144); // msg 144: "Exception not found" } } // ************************************* // D Y N _ m o d i f y _ f u n c t i o n // ************************************* // Its purpose is to change the comment in the function's record and to // allow changing the entry point and/or the module name. void DYN_modify_function(Global* gbl, const UCHAR** ptr) { thread_db* tdbb = JRD_get_thread_data(); Database* dbb = tdbb->getDatabase(); jrd_req* request = CMP_find_request(tdbb, drq_m_fun, DYN_REQUESTS); bool found = false; SqlIdentifier t; GET_STRING(ptr, t); try { FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) X IN RDB$FUNCTIONS WITH X.RDB$FUNCTION_NAME EQ t AND X.RDB$PACKAGE_NAME MISSING if (!DYN_REQUEST(drq_m_fun)) DYN_REQUEST(drq_m_fun) = request; DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, DDL_TRIGGER_ALTER_FUNCTION, t, gbl->sqlText); if (!X.RDB$ENGINE_NAME.NULL) { //// TODO: localize status_exception::raise( Arg::Gds(isc_random) << Arg::Str("Cannot alter new style function with ALTER EXTERNAL FUNCTION")); } found = true; MODIFY X UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) { switch (verb) { case isc_dyn_func_module_name: GET_STRING(ptr, X.RDB$MODULE_NAME); X.RDB$MODULE_NAME.NULL = FALSE; break; case isc_dyn_func_entry_point: GET_STRING(ptr, X.RDB$ENTRYPOINT); X.RDB$ENTRYPOINT.NULL = FALSE; break; default: DYN_unsupported_verb(); } } END_MODIFY; END_FOR; if (!DYN_REQUEST(drq_m_fun)) DYN_REQUEST(drq_m_fun) = request; } catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); DYN_rundown_request(request, -1); DYN_error_punt(true, 92); // msg 92: "MODIFY RDB$FUNCTIONS failed" } if (found) { DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, DDL_TRIGGER_ALTER_FUNCTION, t, gbl->sqlText); } else { DYN_error_punt(false, 41, t); // msg 41: "Function %s not found" } } void DYN_modify_global_field(Global* gbl, const UCHAR** ptr, const MetaName* relation_name, MetaName* field_name) { /************************************** * * D Y N _ m o d i f y _ g l o b a l _ f i e l d * ************************************** * * Functional description * Execute a dynamic ddl statement. * Note: a global field here is a SQL domain. * **************************************/ thread_db* tdbb = JRD_get_thread_data(); Database* dbb = tdbb->getDatabase(); jrd_req* request = CMP_find_request(tdbb, drq_m_gfield, DYN_REQUESTS); bool found = false; dyn_fld orig_dom, new_dom; try { bool dtype, scale, prec, subtype, charlen, collation, fldlen, nullflg, charset; dtype = scale = prec = subtype = charlen = collation = fldlen = nullflg = charset = false; bool bqryname, bqryhdr, bedtstr, bmissingval, bfldvald, bfldvaldsrc, bdelvald, bdeldflt, bflddftval, bflddfltsrc; bqryname = bqryhdr = bedtstr = bmissingval = false; bfldvald = bfldvaldsrc = bdelvald = bdeldflt = bflddftval = bflddfltsrc = false; const UCHAR *qryname, *edtstr; const UCHAR *qryhdr, *missingval, *fldvald, *fldvaldsrc, *flddftval, *flddfltsrc; GET_STRING(ptr, orig_dom.dyn_fld_name); int field_adjusted_count = 0; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME EQ orig_dom.dyn_fld_name.c_str() if (!DYN_REQUEST(drq_m_gfield)) DYN_REQUEST(drq_m_gfield) = request; found = true; DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, DDL_TRIGGER_ALTER_DOMAIN, orig_dom.dyn_fld_name, gbl->sqlText); DSC_make_descriptor(&orig_dom.dyn_dsc, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE, FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE, FLD.RDB$CHARACTER_SET_ID, FLD.RDB$COLLATION_ID); orig_dom.dyn_charbytelen = FLD.RDB$FIELD_LENGTH; orig_dom.dyn_dtype = FLD.RDB$FIELD_TYPE; orig_dom.dyn_precision = FLD.RDB$FIELD_PRECISION; orig_dom.dyn_sub_type = FLD.RDB$FIELD_SUB_TYPE; orig_dom.dyn_charlen = FLD.RDB$CHARACTER_LENGTH; orig_dom.dyn_collation = FLD.RDB$COLLATION_ID; orig_dom.dyn_null_flag = FLD.RDB$NULL_FLAG != 0; // If the original field type is an array, force its blr type to blr_blob bool has_dimensions = false; if (FLD.RDB$DIMENSIONS != 0) { orig_dom.dyn_dtype = blr_blob; has_dimensions = true; } bool single_validate = false; UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) { switch (verb) { case isc_dyn_fld_name: { MetaName newfld; GET_STRING(ptr, newfld); if (!domain_exists(tdbb, gbl, newfld)) { MODIFY FLD USING strcpy(FLD.RDB$FIELD_NAME, newfld.c_str()); FLD.RDB$FIELD_NAME.NULL = FALSE; jrd_req* old_request = request; request = NULL; // CVC: Let's update the dimensions, too. if (has_dimensions) { FOR (REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) DIM_DOM IN RDB$FIELD_DIMENSIONS WITH DIM_DOM.RDB$FIELD_NAME EQ orig_dom.dyn_fld_name.c_str() MODIFY DIM_DOM USING strcpy (DIM_DOM.RDB$FIELD_NAME, newfld.c_str()); DIM_DOM.RDB$FIELD_NAME.NULL = FALSE; END_MODIFY; END_FOR; CMP_release (tdbb, request); request = NULL; } // CVC: End modification. FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) DOM IN RDB$RELATION_FIELDS WITH DOM.RDB$FIELD_SOURCE EQ orig_dom.dyn_fld_name.c_str() MODIFY DOM USING strcpy(DOM.RDB$FIELD_SOURCE, newfld.c_str()); DOM.RDB$FIELD_SOURCE.NULL = FALSE; END_MODIFY; modify_lfield_index(tdbb, gbl, DOM.RDB$RELATION_NAME, DOM.RDB$FIELD_NAME, DOM.RDB$FIELD_NAME); END_FOR; CMP_release(tdbb, request); request = old_request; END_MODIFY; } else { DYN_error_punt(false, 204, SafeArg() << orig_dom.dyn_fld_name.c_str() << newfld.c_str()); // msg 204: Cannot rename domain %s to %s. A domain with that name already exists. } break; } case isc_dyn_rel_name: GET_STRING(ptr, new_dom.dyn_rel_name); break; // CVC: The syntax for DDL alter domain was accepting multiple // changes in one command even to the same features, IE two length alterations. // This repetitive type change will cause havoc so it should be stopped in the future. case isc_dyn_fld_length: fldlen = true; new_dom.dyn_dsc.dsc_length = DYN_get_number(ptr); if (++field_adjusted_count > 2) { EXE_unwind(tdbb, request); DYN_error_punt(false, 148, orig_dom.dyn_fld_name.c_str()); // msg 148: "Only one data type change to the domain %s allowed at a time" } switch (new_dom.dyn_dtype) { case blr_text: case blr_text2: case blr_varying: case blr_varying2: case blr_cstring: case blr_cstring2: new_dom.dyn_charbytelen = new_dom.dyn_dsc.dsc_length; break; default: new_dom.dyn_charbytelen = 0; // It won't be used, anyway. break; } break; case isc_dyn_fld_type: dtype = true; new_dom.dyn_dtype = DYN_get_number(ptr); if (++field_adjusted_count > 2) { EXE_unwind(tdbb, request); DYN_error_punt(false, 148, orig_dom.dyn_fld_name.c_str()); // msg 148: "Only one data type change to the domain %s allowed at a time" } switch (new_dom.dyn_dtype) { case blr_text: case blr_text2: case blr_varying: case blr_varying2: case blr_cstring: case blr_cstring2: if (new_dom.dyn_dsc.dsc_length && !new_dom.dyn_charbytelen) new_dom.dyn_charbytelen = new_dom.dyn_dsc.dsc_length; new_dom.dyn_dsc.dsc_length = DSC_string_length(&new_dom.dyn_dsc); break; case blr_short: new_dom.dyn_dsc.dsc_length = 2; break; case blr_long: case blr_float: new_dom.dyn_dsc.dsc_length = 4; break; case blr_int64: case blr_sql_time: case blr_sql_date: case blr_timestamp: case blr_double: case blr_d_float: new_dom.dyn_dsc.dsc_length = 8; break; default: break; } break; case isc_dyn_fld_scale: scale = true; new_dom.dyn_dsc.dsc_scale = DYN_get_number(ptr); break; case isc_dyn_fld_precision: prec = true; new_dom.dyn_precision = DYN_get_number(ptr); break; case isc_dyn_fld_sub_type: subtype = true; new_dom.dyn_sub_type = DYN_get_number(ptr); break; case isc_dyn_fld_char_length: charlen = true; new_dom.dyn_charlen = DYN_get_number(ptr); break; case isc_dyn_fld_collation: collation = true; new_dom.dyn_collation = DYN_get_number(ptr); break; case isc_dyn_fld_character_set: charset = true; new_dom.dyn_charset = DYN_get_number(ptr); break; case isc_dyn_fld_not_null: nullflg = true; new_dom.dyn_null_flag = true; break; case isc_dyn_fld_query_name: qryname = *ptr; bqryname = true; DYN_skip_attribute(ptr); break; case isc_dyn_fld_query_header: qryhdr = *ptr; bqryhdr = true; DYN_skip_attribute(ptr); break; case isc_dyn_fld_edit_string: edtstr = *ptr; bedtstr = true; DYN_skip_attribute(ptr); break; case isc_dyn_fld_missing_value: missingval = *ptr; bmissingval = true; DYN_skip_attribute(ptr); break; case isc_dyn_single_validation: if (single_validate) { EXE_unwind(tdbb, request); DYN_error_punt(false, 160); // msg 160: "Only one constraint allowed for a domain" break; } single_validate = true; break; case isc_dyn_fld_validation_blr: if (single_validate && (!FLD.RDB$VALIDATION_BLR.NULL)) { EXE_unwind(tdbb, request); DYN_error_punt(false, 160); // msg 160: "Only one constraint allowed for a domain" break; } single_validate = true; fldvald = *ptr; bfldvald = true; DYN_skip_attribute(ptr); break; case isc_dyn_fld_validation_source: fldvaldsrc = *ptr; bfldvaldsrc = true; DYN_skip_attribute(ptr); break; case isc_dyn_del_validation: bdelvald = true; break; case isc_dyn_del_default: bdeldflt = true; break; case isc_dyn_fld_default_value: if (has_dimensions) { DYN_error_punt(false, 226, orig_dom.dyn_fld_name.c_str()); // msg 226: "Default value is not allowed for array type in domain %s" } flddftval = *ptr; bflddftval = true; DYN_skip_attribute(ptr); break; case isc_dyn_fld_default_source: if (has_dimensions) { DYN_error_punt(false, 226, orig_dom.dyn_fld_name.c_str()); // msg 226: "Default value is not allowed for array type in domain %s" } flddfltsrc = *ptr; bflddfltsrc = true; DYN_skip_attribute(ptr); break; case isc_dyn_fld_dimensions: new_dom.dyn_dtype = blr_blob; break; // These should only be defined for BLOB types and should not come through with // any other types. Do nothing with them. case isc_dyn_fld_segment_length: DYN_get_number(ptr); break; default: --(*ptr); DYN_execute(gbl, ptr, relation_name, field_name, NULL, NULL, NULL); } } // Now that we have all of the information needed, let's check to see if the field type can be modifed. // Only do this, however, if we are actually modifying the datatype of the domain. if (dtype) { DSC_make_descriptor(&new_dom.dyn_dsc, new_dom.dyn_dtype, new_dom.dyn_dsc.dsc_scale, new_dom.dyn_dsc.dsc_length, new_dom.dyn_sub_type, new_dom.dyn_charset, new_dom.dyn_collation); const ULONG retval = check_update_fld_type(orig_dom, new_dom); if (retval != FB_SUCCESS) modify_err_punt(tdbb, retval, orig_dom, new_dom); } MODIFY FLD USING if (dtype) { FLD.RDB$FIELD_TYPE = new_dom.dyn_dtype; FLD.RDB$FIELD_TYPE.NULL = FALSE; // If the datatype was changed, update any indexes that involved the domain jrd_req* old_request = request; request = NULL; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) DOM IN RDB$RELATION_FIELDS WITH DOM.RDB$FIELD_SOURCE EQ orig_dom.dyn_fld_name.c_str() modify_lfield_index(tdbb, gbl, DOM.RDB$RELATION_NAME, DOM.RDB$FIELD_NAME, DOM.RDB$FIELD_NAME); END_FOR; CMP_release(tdbb, request); request = old_request; } if (scale) { FLD.RDB$FIELD_SCALE = new_dom.dyn_dsc.dsc_scale; FLD.RDB$FIELD_SCALE.NULL = FALSE; } if (prec) { FLD.RDB$FIELD_PRECISION = new_dom.dyn_precision; FLD.RDB$FIELD_PRECISION.NULL = FALSE; } if (subtype) { FLD.RDB$FIELD_SUB_TYPE = new_dom.dyn_sub_type; FLD.RDB$FIELD_SUB_TYPE.NULL = FALSE; } if (charlen) { FLD.RDB$CHARACTER_LENGTH = new_dom.dyn_charlen; FLD.RDB$CHARACTER_LENGTH.NULL = FALSE; } if (charset) { FLD.RDB$CHARACTER_SET_ID = new_dom.dyn_charset; FLD.RDB$CHARACTER_SET_ID.NULL = FALSE; } if (collation) { FLD.RDB$COLLATION_ID = new_dom.dyn_collation; FLD.RDB$COLLATION_ID.NULL = FALSE; } if (fldlen) { // CVC: Rescue from the wrong field_length with a helper. if (new_dom.dyn_dsc.dsc_dtype <= dtype_varying && new_dom.dyn_charbytelen) FLD.RDB$FIELD_LENGTH = new_dom.dyn_charbytelen; else FLD.RDB$FIELD_LENGTH = new_dom.dyn_dsc.dsc_length; FLD.RDB$FIELD_LENGTH.NULL = FALSE; } if (bqryname) { if (GET_STRING(&qryname, FLD.RDB$QUERY_NAME)) FLD.RDB$QUERY_NAME.NULL = FALSE; else FLD.RDB$QUERY_NAME.NULL = TRUE; } if (bqryhdr) { if (DYN_put_blr_blob(gbl, &qryhdr, &FLD.RDB$QUERY_HEADER)) FLD.RDB$QUERY_HEADER.NULL = FALSE; else FLD.RDB$QUERY_HEADER.NULL = TRUE; } if (bedtstr) { if (GET_STRING(&edtstr, FLD.RDB$EDIT_STRING)) FLD.RDB$EDIT_STRING.NULL = FALSE; else FLD.RDB$EDIT_STRING.NULL = TRUE; } if (bmissingval) { if (DYN_put_blr_blob(gbl, &missingval, &FLD.RDB$MISSING_VALUE)) FLD.RDB$MISSING_VALUE.NULL = FALSE; else FLD.RDB$MISSING_VALUE.NULL = TRUE; } if (bfldvald) { if (DYN_put_blr_blob(gbl, &fldvald, &FLD.RDB$VALIDATION_BLR)) FLD.RDB$VALIDATION_BLR.NULL = FALSE; else FLD.RDB$VALIDATION_BLR.NULL = TRUE; } if (bfldvaldsrc) { if (DYN_put_text_blob(gbl, &fldvaldsrc, &FLD.RDB$VALIDATION_SOURCE)) { FLD.RDB$VALIDATION_SOURCE.NULL = FALSE; } else FLD.RDB$VALIDATION_SOURCE.NULL = TRUE; } if (bdelvald) { FLD.RDB$VALIDATION_BLR.NULL = TRUE; FLD.RDB$VALIDATION_SOURCE.NULL = TRUE; } if (bdeldflt) { FLD.RDB$DEFAULT_VALUE.NULL = TRUE; FLD.RDB$DEFAULT_SOURCE.NULL = TRUE; } if (bflddftval) { if (DYN_put_blr_blob(gbl, &flddftval, &FLD.RDB$DEFAULT_VALUE)) FLD.RDB$DEFAULT_VALUE.NULL = FALSE; else FLD.RDB$DEFAULT_VALUE.NULL = TRUE; } if (bflddfltsrc) { if (DYN_put_text_blob(gbl, &flddfltsrc, &FLD.RDB$DEFAULT_SOURCE)) FLD.RDB$DEFAULT_SOURCE.NULL = FALSE; else FLD.RDB$DEFAULT_SOURCE.NULL = TRUE; } END_MODIFY; END_FOR; if (!DYN_REQUEST(drq_m_gfield)) DYN_REQUEST(drq_m_gfield) = request; } // try catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); DYN_rundown_request(request, -1); DYN_error_punt(true, 87); // msg 87: "MODIFY RDB$FIELDS failed" } if (found) { DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, DDL_TRIGGER_ALTER_DOMAIN, orig_dom.dyn_fld_name, gbl->sqlText); } else { DYN_error_punt(false, 89); // msg 89: "Global field not found" } } void DYN_modify_index( Global* gbl, const UCHAR** ptr) { /************************************** * * D Y N _ m o d i f y _ i n d e x * ************************************** * * Functional description * Modify an existing index * **************************************/ MetaName name; thread_db* tdbb = JRD_get_thread_data(); Database* dbb = tdbb->getDatabase(); jrd_req* request = CMP_find_request(tdbb, drq_m_index, DYN_REQUESTS); bool found = false; try { GET_STRING(ptr, name); FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) IDX IN RDB$INDICES WITH IDX.RDB$INDEX_NAME EQ name.c_str() if (!DYN_REQUEST(drq_m_index)) DYN_REQUEST(drq_m_index) = request; found = true; DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, DDL_TRIGGER_ALTER_INDEX, name, gbl->sqlText); MODIFY IDX USING UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) { switch (verb) { case isc_dyn_idx_unique: IDX.RDB$UNIQUE_FLAG = DYN_get_number(ptr); IDX.RDB$UNIQUE_FLAG.NULL = FALSE; break; case isc_dyn_idx_inactive: IDX.RDB$INDEX_INACTIVE = DYN_get_number(ptr); IDX.RDB$INDEX_INACTIVE.NULL = FALSE; break; // For V4 index selectivity can be set only to -1 case isc_dyn_idx_statistic: IDX.RDB$STATISTICS = -1.0; IDX.RDB$STATISTICS.NULL = FALSE; break; default: DYN_unsupported_verb(); } } END_MODIFY; END_FOR; if (!DYN_REQUEST(drq_m_index)) DYN_REQUEST(drq_m_index) = request; } // try catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); DYN_rundown_request(request, -1); DYN_error_punt(true, 91); // msg 91: "MODIFY RDB$INDICES failed" } if (found) { DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, DDL_TRIGGER_ALTER_INDEX, name, gbl->sqlText); } else { DYN_error_punt(false, 48); // msg 48: "Index not found" } } void DYN_modify_local_field(Global* gbl, const UCHAR** ptr, const MetaName* relation_name) { /************************************** * * D Y N _ m o d i f y _ l o c a l _ f i e l d * ************************************** * * Functional description * Execute a dynamic ddl statement. * **************************************/ USHORT position; MetaName f, r; const UCHAR* query_name; const UCHAR* edit_string; const UCHAR* security_class; const UCHAR* new_name; const UCHAR* query_header; const UCHAR* new_source = NULL; thread_db* tdbb = JRD_get_thread_data(); Database* dbb = tdbb->getDatabase(); GET_STRING(ptr, f); bool sfflag, qnflag, qhflag, esflag, system_flag, scflag, nnflag, ntflag, npflag; sfflag = qnflag = qhflag = esflag = scflag = npflag = nnflag = ntflag = false; TriState nullFlag; UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) { switch (verb) { case isc_dyn_rel_name: GET_STRING(ptr, r); break; case isc_dyn_system_flag: system_flag = DYN_get_number(ptr); sfflag = true; break; case isc_dyn_fld_position: position = DYN_get_number(ptr); npflag = true; break; case isc_dyn_new_fld_name: new_name = *ptr; nnflag = true; DYN_skip_attribute(ptr); break; case isc_dyn_fld_query_name: query_name = *ptr; qnflag = true; DYN_skip_attribute(ptr); break; case isc_dyn_fld_query_header: query_header = *ptr; qhflag = true; DYN_skip_attribute(ptr); break; case isc_dyn_fld_edit_string: edit_string = *ptr; esflag = true; DYN_skip_attribute(ptr); break; case isc_dyn_security_class: security_class = *ptr; scflag = true; DYN_skip_attribute(ptr); break; case isc_dyn_fld_source: new_source = *ptr; DYN_skip_attribute(ptr); break; case isc_dyn_fld_not_null: nullFlag = true; break; case isc_dyn_fld_null: nullFlag = false; break; default: --(*ptr); DYN_execute(gbl, ptr, relation_name, NULL, NULL, NULL, NULL); } } jrd_req* request = CMP_find_request(tdbb, drq_m_lfield, DYN_REQUESTS); bool found = false; try { USHORT existing_position; found = false; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) FLD IN RDB$RELATION_FIELDS WITH FLD.RDB$FIELD_NAME EQ f.c_str() AND FLD.RDB$RELATION_NAME EQ r.c_str() if (!DYN_REQUEST(drq_m_lfield)) DYN_REQUEST(drq_m_lfield) = request; found = true; MODIFY FLD USING if (npflag) existing_position = FLD.RDB$FIELD_POSITION; if (sfflag) { FLD.RDB$SYSTEM_FLAG = system_flag; FLD.RDB$SYSTEM_FLAG.NULL = FALSE; } if (qnflag) { if (GET_STRING(&query_name, FLD.RDB$QUERY_NAME)) FLD.RDB$QUERY_NAME.NULL = FALSE; else FLD.RDB$QUERY_NAME.NULL = TRUE; } if (nnflag) { MetaName new_fld; GET_STRING(&new_name, new_fld); check_view_dependency(tdbb, gbl, r, f); check_sptrig_dependency(tdbb, gbl, r, f); if (!field_exists(tdbb, gbl, r, new_fld)) { strcpy(FLD.RDB$FIELD_NAME, new_fld.c_str()); modify_lfield_index(tdbb, gbl, r, f, FLD.RDB$FIELD_NAME); } else { DYN_error_punt(false, 205, SafeArg() << f.c_str() << new_fld.c_str() << r.c_str()); // msg 205: Cannot rename field %s to %s. A field with that name already exists in table %s. } } if (qhflag) { if (DYN_put_blr_blob(gbl, &query_header, &FLD.RDB$QUERY_HEADER)) FLD.RDB$QUERY_HEADER.NULL = FALSE; else FLD.RDB$QUERY_HEADER.NULL = TRUE; } if (esflag) { if (GET_STRING(&edit_string, FLD.RDB$EDIT_STRING)) FLD.RDB$EDIT_STRING.NULL = FALSE; else FLD.RDB$EDIT_STRING.NULL = TRUE; } if (scflag) { if (GET_STRING(&security_class, FLD.RDB$SECURITY_CLASS)) FLD.RDB$SECURITY_CLASS.NULL = FALSE; else FLD.RDB$SECURITY_CLASS.NULL = TRUE; } if (new_source) GET_STRING(&new_source, FLD.RDB$FIELD_SOURCE); if (nullFlag.isAssigned()) FLD.RDB$NULL_FLAG = (SSHORT) nullFlag.asBool(); END_MODIFY; END_FOR; if (!DYN_REQUEST(drq_m_lfield)) DYN_REQUEST(drq_m_lfield) = request; if (npflag && found && position != existing_position) modify_lfield_position(tdbb, gbl, r, f, position, existing_position); } // try catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); DYN_rundown_request(request, -1); DYN_error_punt(true, 95); // msg 95: "MODIFY RDB$RELATION_FIELDS failed" } if (!found) { DYN_error_punt(false, 176, SafeArg() << f.c_str() << r.c_str()); // msg 176: "column %s does not exist in table/view %s" } } void DYN_modify_relation( Global* gbl, const UCHAR** ptr) { /************************************** * * D Y N _ m o d i f y _ r e l a t i o n * ************************************** * * Functional description * Modify an existing relation * **************************************/ MetaName name, field_name; thread_db* tdbb = JRD_get_thread_data(); Database* dbb = tdbb->getDatabase(); GET_STRING(ptr, name); jrd_req* request = CMP_find_request(tdbb, drq_m_relation, DYN_REQUESTS); bool found = false; try { FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) REL IN RDB$RELATIONS WITH REL.RDB$RELATION_NAME EQ name.c_str() if (!DYN_REQUEST(drq_m_relation)) DYN_REQUEST(drq_m_relation) = request; if (!REL.RDB$VIEW_BLR.NULL) DYN_error_punt(false, 177); DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, DDL_TRIGGER_ALTER_TABLE, name, gbl->sqlText); found = true; MODIFY REL USING UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) switch (verb) { case isc_dyn_system_flag: REL.RDB$SYSTEM_FLAG = DYN_get_number(ptr); REL.RDB$SYSTEM_FLAG.NULL = FALSE; break; case isc_dyn_security_class: if (GET_STRING(ptr, REL.RDB$SECURITY_CLASS)) REL.RDB$SECURITY_CLASS.NULL = FALSE; else REL.RDB$SECURITY_CLASS.NULL = TRUE; break; case isc_dyn_rel_ext_file: if (REL.RDB$EXTERNAL_FILE.NULL) { DYN_rundown_request(request, -1); DYN_error_punt(false, 97); // msg 97: "add EXTERNAL FILE not allowed" } if (!GET_STRING(ptr, REL.RDB$EXTERNAL_FILE)) { DYN_rundown_request(request, -1); DYN_error_punt(false, 98); // msg 98: "drop EXTERNAL FILE not allowed" } break; default: --(*ptr); DYN_execute(gbl, ptr, &name, &field_name, NULL, NULL, NULL); } END_MODIFY; END_FOR; if (!DYN_REQUEST(drq_m_relation)) DYN_REQUEST(drq_m_relation) = request; } // try catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); DYN_rundown_request(request, -1); DYN_error_punt(true, 99); // msg 99: "MODIFY RDB$RELATIONS failed" } if (found) { DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, DDL_TRIGGER_ALTER_TABLE, name, gbl->sqlText); } else { DYN_error_punt(false, 101); // msg 101: "Relation field not found" } } void DYN_modify_view( Global* gbl, const UCHAR** ptr) { /************************************** * * D Y N _ m o d i f y _ v i e w * ************************************** * * Functional description * Execute a dynamic ddl statement. * **************************************/ thread_db* tdbb = JRD_get_thread_data(); Database* dbb = tdbb->getDatabase(); MetaName view_name; GET_STRING(ptr, view_name); jrd_req* request = NULL; bool found = false; try { request = CMP_find_request(tdbb, drq_m_view, DYN_REQUESTS); FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) REL IN RDB$RELATIONS WITH REL.RDB$RELATION_NAME EQ view_name.c_str() AND REL.RDB$VIEW_BLR NOT MISSING if (!DYN_REQUEST(drq_m_view)) DYN_REQUEST(drq_m_view) = request; found = true; DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, DDL_TRIGGER_ALTER_VIEW, view_name, gbl->sqlText); MODIFY REL REL.RDB$SYSTEM_FLAG = 0; REL.RDB$SYSTEM_FLAG.NULL = FALSE; REL.RDB$VIEW_BLR.NULL = TRUE; REL.RDB$VIEW_SOURCE.NULL = TRUE; REL.RDB$SECURITY_CLASS.NULL = TRUE; jrd_req* request2 = NULL; FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE gbl->gbl_transaction) VR IN RDB$VIEW_RELATIONS WITH VR.RDB$VIEW_NAME EQ view_name.c_str() ERASE VR; END_FOR; CMP_release(tdbb, request2); request2 = NULL; FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE gbl->gbl_transaction) TRG IN RDB$TRIGGERS WITH TRG.RDB$RELATION_NAME EQ view_name.c_str() AND TRG.RDB$SYSTEM_FLAG EQ fb_sysflag_view_check ERASE TRG; END_FOR CMP_release(tdbb, request2); UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) { switch (verb) { case isc_dyn_system_flag: REL.RDB$SYSTEM_FLAG = DYN_get_number(ptr); REL.RDB$SYSTEM_FLAG.NULL = FALSE; break; case isc_dyn_view_blr: if (DYN_put_blr_blob(gbl, ptr, &REL.RDB$VIEW_BLR)) REL.RDB$VIEW_BLR.NULL = FALSE; else REL.RDB$VIEW_BLR.NULL = TRUE; break; case isc_dyn_view_source: if (DYN_put_text_blob(gbl, ptr, &REL.RDB$VIEW_SOURCE)) REL.RDB$VIEW_SOURCE.NULL = FALSE; else REL.RDB$VIEW_SOURCE.NULL = TRUE; break; case isc_dyn_security_class: GET_STRING(ptr, REL.RDB$SECURITY_CLASS); REL.RDB$SECURITY_CLASS.NULL = FALSE; break; default: --(*ptr); MetaTmp(REL.RDB$RELATION_NAME) DYN_execute(gbl, ptr, &tmp, NULL, NULL, NULL, NULL); } } END_MODIFY; END_FOR; if (!DYN_REQUEST(drq_m_view)) DYN_REQUEST(drq_m_view) = request; } // try catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); DYN_rundown_request(request, -1); DYN_error_punt(true, 99); // msg 99: "MODIFY RDB$RELATIONS failed" } if (found) { DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, DDL_TRIGGER_ALTER_VIEW, view_name, gbl->sqlText); } else { DYN_error_punt(false, 54, view_name.c_str()); // msg 54: "View %s not found" } } static void change_backup_mode( Global* gbl, UCHAR verb) { /************************************** * * c h a n g e _ b a c k u p _ m o d e * ************************************** * * Functional description * Drop backup difference file for the database, * begin or end backup * **************************************/ bool invalid_state = false; thread_db* tdbb = JRD_get_thread_data(); Database* dbb = tdbb->getDatabase(); if (!tdbb->getAttachment()->locksmith()) { ERR_post(Arg::Gds(isc_adm_task_denied)); } jrd_req* request = CMP_find_request(tdbb, drq_d_difference, DYN_REQUESTS); bool found = false; try { FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) X IN RDB$FILES if (X.RDB$FILE_FLAGS & FILE_difference) { found = true; switch (verb) { case isc_dyn_drop_difference: ERASE X; break; case isc_dyn_begin_backup: if (X.RDB$FILE_FLAGS & FILE_backing_up) { invalid_state = true; } else { MODIFY X USING X.RDB$FILE_FLAGS |= FILE_backing_up; END_MODIFY; } break; case isc_dyn_end_backup: if (X.RDB$FILE_FLAGS & FILE_backing_up) { if (X.RDB$FILE_NAME.NULL) { ERASE X; } else { MODIFY X USING X.RDB$FILE_FLAGS &= ~FILE_backing_up; END_MODIFY; } } else { invalid_state = true; } break; } } END_FOR; if (!DYN_REQUEST(drq_d_difference)) DYN_REQUEST(drq_d_difference) = request; } catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); DYN_rundown_request(request, drq_d_difference); DYN_error_punt(true, 63); // msg 63: ERASE RDB$FILE failed } if (!found && verb == isc_dyn_begin_backup) { try { request = CMP_find_request(tdbb, drq_s2_difference, DYN_REQUESTS); STORE(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) X IN RDB$FILES X.RDB$FILE_NAME.NULL = TRUE; X.RDB$FILE_FLAGS.NULL = FALSE; X.RDB$FILE_FLAGS = FILE_difference | FILE_backing_up; X.RDB$FILE_START = 0; X.RDB$FILE_START.NULL = FALSE; X.RDB$FILE_LENGTH.NULL = TRUE; X.RDB$SHADOW_NUMBER.NULL = TRUE; END_STORE; found = true; if (!DYN_REQUEST(drq_s2_difference)) { DYN_REQUEST(drq_s2_difference) = request; } } catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); DYN_rundown_request(request, drq_s2_difference); DYN_error_punt(true, 150); // msg 150: STORE RDB$FILES failed } } if (invalid_state) { DYN_error_punt(false, verb == isc_dyn_begin_backup ? 217 : 218); // msg 217: "Database is already in the physical backup mode" // msg 218: "Database is not in the physical backup mode" } if (!found) { DYN_error_punt(false, verb == isc_dyn_end_backup ? 218 : 215); // msg 218: "Database is not in the physical backup mode" // msg 215: "Difference file is not defined" } } static void modify_lfield_position(thread_db* tdbb, Global* gbl, const MetaName& relation_name, const MetaName& field_name, USHORT new_position, USHORT existing_position) { /*********************************************************** * * m o d i f y _ l f i e l d _ p o s i t i o n *********************************************************** * * Functional Description: * 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 * ***********************************************************/ jrd_req* request = NULL; try { // Find the position of the last field in the relation SLONG max_position = -1; DYN_UTIL_generate_field_position(tdbb, gbl, relation_name, &max_position); // 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 move_down = false; if (existing_position < new_position) move_down = true; // Retrieve the records for the fields which have a position between the // existing field position and the new field position FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) FLD IN RDB$RELATION_FIELDS WITH FLD.RDB$RELATION_NAME EQ relation_name.c_str() AND FLD.RDB$FIELD_POSITION >= MIN(new_position, existing_position) AND FLD.RDB$FIELD_POSITION <= MAX(new_position, existing_position) MODIFY FLD USING // If the field is the one we want, change the position, otherwise // increase the value of RDB$FIELD_POSITION if (field_name == FLD.RDB$FIELD_NAME) { if (new_position > max_position) { // This prevents gaps in the position sequence of the fields FLD.RDB$FIELD_POSITION = max_position; } else FLD.RDB$FIELD_POSITION = new_position; } else { if (move_down) 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; CMP_release(tdbb, request); request = NULL; // Once the field position has been changed, make sure that there are no // duplicate field positions and no gaps in the position sequence (this can // not be guaranteed by the query above USHORT new_pos = 0; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) FLD IN RDB$RELATION_FIELDS WITH FLD.RDB$RELATION_NAME EQ relation_name.c_str() SORTED BY ASCENDING FLD.RDB$FIELD_POSITION if (FLD.RDB$FIELD_POSITION != new_pos) { MODIFY FLD USING FLD.RDB$FIELD_POSITION = new_pos; END_MODIFY; } new_pos += 1; END_FOR; CMP_release(tdbb, request); } catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); DYN_error_punt(true, 95); // msg 95: "MODIFY RDB$RELATION_FIELDS failed" } } static bool check_view_dependency(thread_db* tdbb, Global* gbl, const MetaName& relation_name, const MetaName& field_name) { /*********************************************************** * * c h e c k _ v i e w _ d e p e n d e n c y *********************************************************** * * Functional Description: * Checks to see if the given field is referenced in a view. If the field * is referenced in a view, return true, else return false * CVC: The function never has a chance to return true because it punts. * ***********************************************************/ jrd_req* request = NULL; bool retval = false; MetaName view_name; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_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 relation_name.c_str() AND X.RDB$FIELD_NAME EQ field_name.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 retval = true; view_name = Z.RDB$VIEW_NAME; END_FOR; CMP_release(tdbb, request); if (retval) { DYN_error_punt(false, 206, SafeArg() << field_name.c_str() << relation_name.c_str() << view_name.c_str()); // msg 206: Column %s from table %s is referenced in %s. } return retval; } static bool check_sptrig_dependency(thread_db* tdbb, Global* gbl, const MetaName& relation_name, const MetaName& field_name) { /*********************************************************** * * c h e c k _ s p t r i g _ d e p e n d e n c y *********************************************************** * * Functional Description: * Checks to see if the given field is referenced in a stored procedure * or trigger. If the field is referenced, return true, else return * false, but true causes the function to punt instead. ***********************************************************/ jrd_req* request = NULL; bool retval = false; MetaName dep_name; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) FIRST 1 DEP IN RDB$DEPENDENCIES WITH DEP.RDB$DEPENDED_ON_NAME EQ relation_name.c_str() AND DEP.RDB$FIELD_NAME EQ field_name.c_str() retval = true; dep_name = DEP.RDB$DEPENDENT_NAME; END_FOR; CMP_release(tdbb, request); if (retval) { DYN_error_punt(false, 206, SafeArg() << field_name.c_str() << relation_name.c_str() << dep_name.c_str()); // msg 206: Column %s from table %s is referenced in %s. } return retval; } static void modify_lfield_index(thread_db* tdbb, Global* gbl, const MetaName& relation_name, const MetaName& field_name, const MetaName& new_fld_name) { /*********************************************************** * * m o d i f y _ l f i e l d _ i n d e x *********************************************************** * * Functional Description: * Updates the field names in an index and forces the index to be rebuilt * with the new field names * ***********************************************************/ jrd_req* request = NULL; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) IDX IN RDB$INDICES CROSS IDXS IN RDB$INDEX_SEGMENTS WITH IDX.RDB$INDEX_NAME EQ IDXS.RDB$INDEX_NAME AND IDX.RDB$RELATION_NAME EQ relation_name.c_str() AND IDXS.RDB$FIELD_NAME EQ field_name.c_str() // Change the name of the field in the index MODIFY IDXS USING memcpy(IDXS.RDB$FIELD_NAME, new_fld_name.c_str(), sizeof(IDXS.RDB$FIELD_NAME)); END_MODIFY; // Set the index name to itself to tell the index to rebuild MODIFY IDX USING // This is to fool both gpre and gcc. char* p = IDX.RDB$INDEX_NAME; p[MAX_SQL_IDENTIFIER_LEN] = 0; END_MODIFY; END_FOR; CMP_release(tdbb, request); } static bool field_exists(thread_db* tdbb, Global* gbl, const MetaName& relation_name, const MetaName& field_name) { /*********************************************************** * * f i e l d _ e x i s t s *********************************************************** * * Functional Description: * Checks to see if the given field already exists in a relation ***********************************************************/ jrd_req* request = NULL; bool retval = false; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) FLD IN RDB$RELATION_FIELDS WITH FLD.RDB$RELATION_NAME EQ relation_name.c_str() AND FLD.RDB$FIELD_NAME EQ field_name.c_str() retval = true; END_FOR; CMP_release(tdbb, request); return retval; } static bool domain_exists(thread_db* tdbb, Global* gbl, const MetaName& field_name) { /*********************************************************** * * d o m a i n _ e x i s t s *********************************************************** * * Functional Description: * Checks to see if the given field already exists in a relation ***********************************************************/ jrd_req* request = NULL; bool retval = false; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME EQ field_name.c_str() retval = true; END_FOR; CMP_release(tdbb, request); return retval; } void DYN_modify_sql_field(Global* gbl, const UCHAR** ptr, const MetaName* relation_name) { /************************************** * * D Y N _ m o d i f y _ s q l _ f i e l d * ************************************** * * Functional description * Execute a dynamic ddl statement * to modify the datatype of a field. * * If there are dependencies on the field, abort the operation * unless the dependency is an index. In this case, rebuild the * index once the operation has completed. * * If the original datatype of the field was a domain: * if the new type is a domain, just make the change to the new domain * if it exists * * if the new type is a base type, just make the change * * If the original datatype of the field was a base type: * if the new type is a base type, just make the change * * if the new type is a domain, make the change to the field * definition and remove the entry for RDB$FIELD_SOURCE from the original * field. In other words ... clean up after ourselves * * The following conversions are not allowed: * Blob to anything * Array to anything * Date to anything * Char to any numeric * Varchar to any numeric * Anything to Blob * Anything to Array * * The following operations return a warning * Decreasing the length of a char (varchar) field * * CVC: This is a misleading comment. There's no code that * produces a warning. This condition raises an error, too. * **************************************/ thread_db* tdbb = JRD_get_thread_data(); dyn_fld orig_fld, new_fld, dom_fld; try { GET_STRING(ptr, orig_fld.dyn_fld_name); // Check to see if the field being altered is involved in any type of dependency. // If there is a dependency, call DYN_error_punt (inside the function). fb_assert(relation_name); // ASF: check disabled to allow change of field type to be used // with TYPE OF COLUMN table.column feature. //check_sptrig_dependency(tdbb, gbl, *relation_name, // orig_fld.dyn_fld_name); jrd_req* request = NULL; jrd_req* first_request = NULL; bool found = false; bool dtype, scale, prec, subtype, charlen, collation, fldlen, nullflg, charset; dtype = scale = prec = subtype = charlen = collation = fldlen = nullflg = charset = false; int field_adjusted_count = 0; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) RFR IN RDB$RELATION_FIELDS CROSS REL IN RDB$RELATIONS WITH REL.RDB$RELATION_NAME = RFR.RDB$RELATION_NAME AND RFR.RDB$RELATION_NAME = relation_name->c_str() AND RFR.RDB$FIELD_NAME = orig_fld.dyn_fld_name.c_str() first_request = request; request = NULL; found = true; bool is_view = !REL.RDB$VIEW_BLR.NULL; bool has_dimensions = false; bool update_domain = false; bool domain_has_default = false; bool domain_is_computed = false; SSHORT fld_position = 0; bool fld_position_change = false; SSHORT view_context = 0; bool view_context_change = false; MetaName fld_base_field; bool fld_base_field_change = false; bool orig_computed = false; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME = RFR.RDB$FIELD_SOURCE // Get information about the original field type. If a conversion // can not be at any time made between the two datatypes, error. DSC_make_descriptor(&orig_fld.dyn_dsc, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE, FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE, FLD.RDB$CHARACTER_SET_ID, FLD.RDB$COLLATION_ID); orig_fld.dyn_charbytelen = FLD.RDB$FIELD_LENGTH; orig_fld.dyn_dtype = FLD.RDB$FIELD_TYPE; orig_fld.dyn_precision = FLD.RDB$FIELD_PRECISION; orig_fld.dyn_sub_type = FLD.RDB$FIELD_SUB_TYPE; orig_fld.dyn_charlen = FLD.RDB$CHARACTER_LENGTH; orig_fld.dyn_collation = FLD.RDB$COLLATION_ID; orig_fld.dyn_null_flag = FLD.RDB$NULL_FLAG != 0; orig_fld.dyn_fld_source = RFR.RDB$FIELD_SOURCE; orig_computed = !FLD.RDB$COMPUTED_BLR.NULL; // If the original field type is an array, force its blr type to blr_blob if (FLD.RDB$DIMENSIONS != 0) { orig_fld.dyn_dtype = blr_blob; has_dimensions = true; } domain_has_default = !FLD.RDB$DEFAULT_VALUE.NULL; domain_is_computed = !FLD.RDB$COMPUTED_BLR.NULL; UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) { switch (verb) { case isc_dyn_fld_source: GET_STRING(ptr, dom_fld.dyn_fld_source); if (fb_utils::implicit_domain(dom_fld.dyn_fld_source.c_str())) { DYN_error_punt(false, 224, SafeArg() << dom_fld.dyn_fld_source.c_str() << orig_fld.dyn_fld_name.c_str()); // msg 224: "Cannot use the internal domain %s as new type for field %s". } get_domain_type(tdbb, gbl, dom_fld); update_domain = true; break; case isc_dyn_rel_name: GET_STRING(ptr, new_fld.dyn_rel_name); break; case isc_dyn_fld_length: fldlen = true; new_fld.dyn_dsc.dsc_length = DYN_get_number(ptr); if (++field_adjusted_count > 2) { DYN_error_punt(false, 149, orig_fld.dyn_fld_name.c_str()); // msg 149: "Only one data type change to the field %s allowed at a time" } switch (new_fld.dyn_dtype) { case blr_text: case blr_text2: case blr_varying: case blr_varying2: case blr_cstring: case blr_cstring2: new_fld.dyn_charbytelen = new_fld.dyn_dsc.dsc_length; break; default: new_fld.dyn_charbytelen = 0; // It won't be used, anyway. break; } break; case isc_dyn_fld_type: dtype = true; new_fld.dyn_dtype = DYN_get_number(ptr); if (++field_adjusted_count > 2) { DYN_error_punt(false, 149, orig_fld.dyn_fld_name.c_str()); // msg 149: "Only one data type change to the field %s allowed at a time" } switch (new_fld.dyn_dtype) { case blr_text: case blr_text2: case blr_varying: case blr_varying2: case blr_cstring: case blr_cstring2: if (new_fld.dyn_dsc.dsc_length && !new_fld.dyn_charbytelen) new_fld.dyn_charbytelen = new_fld.dyn_dsc.dsc_length; new_fld.dyn_dsc.dsc_length = DSC_string_length (&new_fld.dyn_dsc); break; case blr_short: new_fld.dyn_dsc.dsc_length = 2; break; case blr_long: case blr_float: new_fld.dyn_dsc.dsc_length = 4; break; case blr_int64: case blr_sql_time: case blr_sql_date: case blr_timestamp: case blr_double: case blr_d_float: new_fld.dyn_dsc.dsc_length = 8; break; default: break; } break; case isc_dyn_fld_scale: scale = true; new_fld.dyn_dsc.dsc_scale = DYN_get_number(ptr); break; case isc_dyn_fld_precision: prec = true; new_fld.dyn_precision = DYN_get_number(ptr); break; case isc_dyn_fld_sub_type: subtype = true; new_fld.dyn_sub_type = DYN_get_number(ptr); break; case isc_dyn_fld_char_length: charlen = true; new_fld.dyn_charlen = DYN_get_number(ptr); break; case isc_dyn_fld_collation: collation = true; new_fld.dyn_collation = DYN_get_number(ptr); break; case isc_dyn_fld_character_set: charset = true; new_fld.dyn_charset = DYN_get_number(ptr); break; case isc_dyn_fld_not_null: nullflg = true; new_fld.dyn_null_flag = true; break; case isc_dyn_fld_dimensions: new_fld.dyn_dtype = blr_blob; break; case isc_dyn_fld_position: fld_position = DYN_get_number(ptr); fld_position_change = true; break; case isc_dyn_fld_base_fld: GET_STRING(ptr, fld_base_field); fld_base_field_change = true; break; case isc_dyn_view_context: view_context = DYN_get_number(ptr); view_context_change = true; break; case isc_dyn_fld_computed_blr: domain_is_computed = true; new_fld.dyn_computed_val = *ptr; DYN_skip_attribute(ptr); break; case isc_dyn_fld_computed_source: new_fld.dyn_computed_src = *ptr; DYN_skip_attribute(ptr); break; case isc_dyn_del_computed: domain_is_computed = false; new_fld.dyn_drop_computed = true; break; case isc_dyn_del_default: new_fld.dyn_drop_default = true; break; case isc_dyn_fld_default_value: if (has_dimensions) { DYN_error_punt(false, 225, orig_fld.dyn_fld_name.c_str()); // msg 225: "Default value is not allowed for array type in field %s" } new_fld.dyn_default_val = *ptr; DYN_skip_attribute(ptr); break; case isc_dyn_fld_default_source: if (has_dimensions) { DYN_error_punt(false, 225, orig_fld.dyn_fld_name.c_str()); // msg 225: "Default value is not allowed for array type in field %s" } new_fld.dyn_default_src = *ptr; DYN_skip_attribute(ptr); break; // These should only be defined for BLOB types and should not come through with // any other types. BLOB types are detected above case isc_dyn_fld_segment_length: DYN_get_number(ptr); break; default: --(*ptr); MetaTmp(RFR.RDB$FIELD_SOURCE) DYN_execute(gbl, ptr, relation_name, &tmp, NULL, NULL, NULL); } } if (fld_base_field_change && view_context_change) { fb_assert(is_view); if (fld_base_field.hasData()) { char field_name[MAX_SQL_IDENTIFIER_SIZE]; DYN_UTIL_find_field_source(tdbb, gbl, RFR.RDB$RELATION_NAME, view_context, fld_base_field.c_str(), field_name); dom_fld.dyn_fld_source = field_name; update_domain = true; } else DYN_UTIL_generate_field_name(tdbb, gbl, new_fld.dyn_fld_source); } END_FOR; // FLD in RDB$FIELDS CMP_release(tdbb, request); request = NULL; if (!is_view && ((new_fld.dyn_computed_val && !orig_computed) || (!new_fld.dyn_computed_val && orig_computed))) { // Cannot add or remove COMPUTED from column @1 DYN_error_punt(false, 249, SafeArg() << orig_fld.dyn_fld_name.c_str()); } const bool sourceIsInternalDomain = fb_utils::implicit_domain(orig_fld.dyn_fld_source.c_str()) && RFR.RDB$BASE_FIELD.NULL; const bool changeComputed = new_fld.dyn_computed_val || new_fld.dyn_drop_computed; // Now that we have all of the information needed, let's check to see // if the field type can be modified if (update_domain) { // a1. Internal domain -> domain. // a2. Domain -> domain. /* CVC: Since get_domain_type() called above already called DSC_make_descriptor, there's no point in calling it again, since it will increment AGAIN the length of varchar fields! This bug detected thanks to new check field dyn_charbytelen. DSC_make_descriptor(&dom_fld.dyn_dsc, dom_fld.dyn_dtype, dom_fld.dyn_dsc.dsc_scale, dom_fld.dyn_dsc.dsc_length, dom_fld.dyn_sub_type, dom_fld.dyn_charset, dom_fld.dyn_collation); */ if (!domain_is_computed && !is_view) { const ULONG retval = check_update_fld_type(orig_fld, dom_fld); if (retval != FB_SUCCESS) modify_err_punt(tdbb, retval, orig_fld, dom_fld); } // If the original definition was a base field type, remove the entries from RDB$FIELDS if (sourceIsInternalDomain) { // a1. Internal domain -> domain. FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME = RFR.RDB$FIELD_SOURCE ERASE FLD; END_FOR; CMP_release(tdbb, request); request = NULL; } request = first_request; MODIFY RFR USING strcpy(RFR.RDB$FIELD_SOURCE, dom_fld.dyn_fld_source.c_str()); RFR.RDB$FIELD_SOURCE.NULL = FALSE; if (domain_is_computed) { RFR.RDB$UPDATE_FLAG.NULL = FALSE; RFR.RDB$UPDATE_FLAG = 1; } RFR.RDB$COLLATION_ID.NULL = TRUE; // CORE-2426 END_MODIFY; first_request = request; request = NULL; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) PRM IN RDB$PROCEDURE_PARAMETERS WITH PRM.RDB$RELATION_NAME = relation_name->c_str() AND PRM.RDB$FIELD_NAME = orig_fld.dyn_fld_name.c_str() MODIFY PRM USING strcpy(PRM.RDB$FIELD_SOURCE, dom_fld.dyn_fld_source.c_str()); END_MODIFY; END_FOR; CMP_release(tdbb, request); request = NULL; } else { // b1. Domain -> internal domain. // b2. Internal domain -> internal domain. const UCHAR* defVal = new_fld.dyn_default_val; const UCHAR* defSrc = new_fld.dyn_default_src; const bool dropDefault = new_fld.dyn_drop_default; const bool changeDefault = defVal || defSrc || dropDefault; // If we are altering only the default, we need the old field settings. if (changeDefault && !new_fld.dyn_dsc.dsc_dtype) { new_fld = orig_fld; // We already called DSC_make_descriptor on orig_fld, so fix new_fld. switch (new_fld.dyn_dtype) { case blr_text: case blr_varying: case blr_cstring: new_fld.dyn_dsc.dsc_length = DSC_string_length(&orig_fld.dyn_dsc); break; } } DSC_make_descriptor(&new_fld.dyn_dsc, new_fld.dyn_dtype, new_fld.dyn_dsc.dsc_scale, new_fld.dyn_dsc.dsc_length, new_fld.dyn_sub_type, new_fld.dyn_charset, new_fld.dyn_collation); if (!changeComputed && !orig_computed && !is_view) { const ULONG retval = check_update_fld_type(orig_fld, new_fld); if (retval != FB_SUCCESS) modify_err_punt(tdbb, retval, orig_fld, new_fld); } // Check to see if the original data type for the field was based on a domain. If it // was (and now it isn't), remove the domain information and replace it with a generated // field name for RDB$FIELDS if (!changeDefault && !sourceIsInternalDomain) { // b1. Domain -> internal domain. Not for changing DEFAULT value. request = first_request; MODIFY RFR USING DYN_UTIL_generate_field_name(tdbb, gbl, RFR.RDB$FIELD_SOURCE); new_fld.dyn_fld_source = RFR.RDB$FIELD_SOURCE; END_MODIFY; first_request = request; request = NULL; STORE(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) FLD IN RDB$FIELDS FLD.RDB$SYSTEM_FLAG = 0; FLD.RDB$SYSTEM_FLAG.NULL = FALSE; FLD.RDB$FIELD_SCALE.NULL = TRUE; FLD.RDB$FIELD_PRECISION.NULL = TRUE; FLD.RDB$FIELD_SUB_TYPE.NULL = TRUE; FLD.RDB$SEGMENT_LENGTH.NULL = TRUE; FLD.RDB$COMPUTED_BLR.NULL = TRUE; FLD.RDB$COMPUTED_SOURCE.NULL = TRUE; FLD.RDB$DEFAULT_VALUE.NULL = TRUE; FLD.RDB$DEFAULT_SOURCE.NULL = TRUE; FLD.RDB$VALIDATION_BLR.NULL = TRUE; FLD.RDB$VALIDATION_SOURCE.NULL = TRUE; FLD.RDB$NULL_FLAG.NULL = TRUE; FLD.RDB$EDIT_STRING.NULL = TRUE; FLD.RDB$DIMENSIONS.NULL = TRUE; FLD.RDB$CHARACTER_LENGTH.NULL = TRUE; FLD.RDB$CHARACTER_SET_ID.NULL = TRUE; FLD.RDB$COLLATION_ID.NULL = TRUE; if (dtype) { FLD.RDB$FIELD_TYPE = new_fld.dyn_dtype; FLD.RDB$FIELD_TYPE.NULL = FALSE; } if (scale) { FLD.RDB$FIELD_SCALE = new_fld.dyn_dsc.dsc_scale; FLD.RDB$FIELD_SCALE.NULL = FALSE; } if (prec) { FLD.RDB$FIELD_PRECISION = new_fld.dyn_precision; FLD.RDB$FIELD_PRECISION.NULL = FALSE; } if (subtype) { FLD.RDB$FIELD_SUB_TYPE = new_fld.dyn_sub_type; FLD.RDB$FIELD_SUB_TYPE.NULL = FALSE; } if (charlen) { FLD.RDB$CHARACTER_LENGTH = new_fld.dyn_charlen; FLD.RDB$CHARACTER_LENGTH.NULL = FALSE; } if (charset) { FLD.RDB$CHARACTER_SET_ID = new_fld.dyn_charset; FLD.RDB$CHARACTER_SET_ID.NULL = FALSE; } if (collation) { FLD.RDB$COLLATION_ID = new_fld.dyn_collation; FLD.RDB$COLLATION_ID.NULL = FALSE; } if (fldlen) { // CVC: Rescue from the wrong field_length with a helper. if (new_fld.dyn_dsc.dsc_dtype <= dtype_varying && new_fld.dyn_charbytelen) FLD.RDB$FIELD_LENGTH = new_fld.dyn_charbytelen; else FLD.RDB$FIELD_LENGTH = new_fld.dyn_dsc.dsc_length; FLD.RDB$FIELD_LENGTH.NULL = FALSE; } if (new_fld.dyn_computed_val) { DYN_put_blr_blob(gbl, &new_fld.dyn_computed_val, &FLD.RDB$COMPUTED_BLR); FLD.RDB$COMPUTED_BLR.NULL = FALSE; } if (new_fld.dyn_computed_src) { DYN_put_text_blob(gbl, &new_fld.dyn_computed_src, &FLD.RDB$COMPUTED_SOURCE); FLD.RDB$COMPUTED_SOURCE.NULL = FALSE; } // Copy the field name into RDB$FIELDS strcpy(FLD.RDB$FIELD_NAME, new_fld.dyn_fld_source.c_str()); END_STORE; CMP_release(tdbb, request); request = NULL; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) PRM IN RDB$PROCEDURE_PARAMETERS WITH PRM.RDB$RELATION_NAME = relation_name->c_str() AND PRM.RDB$FIELD_NAME = orig_fld.dyn_fld_name.c_str() MODIFY PRM USING strcpy(PRM.RDB$FIELD_SOURCE, new_fld.dyn_fld_source.c_str()); END_MODIFY; END_FOR; CMP_release(tdbb, request); request = NULL; } else if (changeDefault) { request = first_request; MODIFY RFR USING if (dropDefault) { if (RFR.RDB$DEFAULT_VALUE.NULL) { if (sourceIsInternalDomain || !domain_has_default) { DYN_error_punt(false, 229, orig_fld.dyn_fld_name.c_str()); // msg 229: "Local column %s doesn't have a default" } else if (domain_has_default) { DYN_error_punt(false, 230, SafeArg() << orig_fld.dyn_fld_name.c_str() << orig_fld.dyn_fld_source.c_str()); // msg 230: "Local column %s default belongs to domain %s" } } else { RFR.RDB$DEFAULT_VALUE.NULL = TRUE; RFR.RDB$DEFAULT_SOURCE.NULL = TRUE; } } else { if (domain_is_computed) { DYN_error_punt(false, 233, orig_fld.dyn_fld_name.c_str()); // msg 233: "Local column %s is computed, cannot set a default value" } if (defVal) { const UCHAR* p = defVal; if (DYN_put_blr_blob(gbl, &p, &RFR.RDB$DEFAULT_VALUE)) RFR.RDB$DEFAULT_VALUE.NULL = FALSE; } if (defSrc) { const UCHAR* p = defSrc; if (DYN_put_text_blob(gbl, &p, &RFR.RDB$DEFAULT_SOURCE)) RFR.RDB$DEFAULT_SOURCE.NULL = FALSE; } } END_MODIFY first_request = request; request = NULL; } else //if (sourceIsInternalDomain) { // The original and new definitions are both base types. // b2. Internal domain -> internal domain. // Modify in place, since there cannot be other fields using it. //if (!sourceIsInternalDomain) // DYN_UTIL_copy_domain(tdbb, gbl, orig_fld.dyn_fld_source, new_fld.dyn_fld_source); FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME = RFR.RDB$FIELD_SOURCE MODIFY FLD USING if (dtype) { FLD.RDB$FIELD_TYPE = new_fld.dyn_dtype; FLD.RDB$FIELD_TYPE.NULL = FALSE; } if (scale) { FLD.RDB$FIELD_SCALE = new_fld.dyn_dsc.dsc_scale; FLD.RDB$FIELD_SCALE.NULL = FALSE; } if (prec) { FLD.RDB$FIELD_PRECISION = new_fld.dyn_precision; FLD.RDB$FIELD_PRECISION.NULL = FALSE; } if (subtype) { FLD.RDB$FIELD_SUB_TYPE = new_fld.dyn_sub_type; FLD.RDB$FIELD_SUB_TYPE.NULL = FALSE; } if (charlen) { FLD.RDB$CHARACTER_LENGTH = new_fld.dyn_charlen; FLD.RDB$CHARACTER_LENGTH.NULL = FALSE; } if (charset) { FLD.RDB$CHARACTER_SET_ID = new_fld.dyn_charset; FLD.RDB$CHARACTER_SET_ID.NULL = FALSE; } if (collation) { FLD.RDB$COLLATION_ID = new_fld.dyn_collation; FLD.RDB$COLLATION_ID.NULL = FALSE; } if (fldlen) { // CVC: Rescue from the wrong field_length with a helper. if (new_fld.dyn_dsc.dsc_dtype <= dtype_varying && new_fld.dyn_charbytelen) FLD.RDB$FIELD_LENGTH = new_fld.dyn_charbytelen; else FLD.RDB$FIELD_LENGTH = new_fld.dyn_dsc.dsc_length; FLD.RDB$FIELD_LENGTH.NULL = FALSE; } if (changeComputed) { FLD.RDB$DEFAULT_SOURCE.NULL = TRUE; FLD.RDB$DEFAULT_VALUE.NULL = TRUE; if (new_fld.dyn_computed_val) { DYN_put_blr_blob(gbl, &new_fld.dyn_computed_val, &FLD.RDB$COMPUTED_BLR); FLD.RDB$COMPUTED_BLR.NULL = FALSE; } if (new_fld.dyn_computed_src) { DYN_put_text_blob(gbl, &new_fld.dyn_computed_src, &FLD.RDB$COMPUTED_SOURCE); FLD.RDB$COMPUTED_SOURCE.NULL = FALSE; } } END_MODIFY; END_FOR; // FLD in RDB$FIELDS CMP_release(tdbb, request); if (domain_is_computed) { request = first_request; MODIFY RFR USING RFR.RDB$UPDATE_FLAG.NULL = FALSE; RFR.RDB$UPDATE_FLAG = 1; END_MODIFY; first_request = request; } request = NULL; } } // else not a domain request = first_request; if (fld_position_change || view_context_change || fld_base_field_change) { MODIFY RFR USING if (fld_position_change) RFR.RDB$FIELD_POSITION = fld_position; if (view_context_change) RFR.RDB$VIEW_CONTEXT = view_context; if (fld_base_field_change) { RFR.RDB$BASE_FIELD.NULL = fld_base_field.isEmpty(); strcpy(RFR.RDB$BASE_FIELD, fld_base_field.c_str()); } END_MODIFY; } END_FOR; // RFR IN RDB$RELATION_FIELDS CMP_release(tdbb, request); request = NULL; if (!found) { DYN_error_punt(false, 176, SafeArg() << orig_fld.dyn_fld_name.c_str() << relation_name->c_str()); // msg 176: "column %s does not exist in table/view %s" } // Update any indices that exist modify_lfield_index(tdbb, gbl, *relation_name, orig_fld.dyn_fld_name, orig_fld.dyn_fld_name); } catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); DYN_error_punt(true, 95); // msg 95: "MODIFY RDB$RELATION_FIELDS failed" } } // ************************************* // D Y N _ m o d i f y _ m a p p i n g // ************************************* // It's purpose is to add/drop mapping from OS security name // to DB security object void DYN_modify_mapping(Global* gbl, const UCHAR** ptr) { thread_db* tdbb = JRD_get_thread_data(); Database* dbb = tdbb->getDatabase(); jrd_req* request = CMP_find_request(tdbb, drq_m_map, DYN_REQUESTS); bool found = false; string osName, dbName; GET_STRING(ptr, osName); const UCHAR op = *(*ptr)++; GET_STRING(ptr, dbName); // This is FB 2.5 limited implementation! // Later it should work with new system table, something like RDB$MAPPING. if (dbName != ADMIN_ROLE) { status_exception::raise(Arg::Gds(isc_no_meta_update) << Arg::Gds(isc_wish_list)); } if (!(tdbb->getAttachment() && tdbb->getAttachment()->locksmith())) ERR_post(Arg::Gds(isc_adm_task_denied)); found = false; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) X IN RDB$ROLES WITH X.RDB$ROLE_NAME EQ dbName.c_str() if (!DYN_REQUEST(drq_m_map)) DYN_REQUEST(drq_m_map) = request; found = true; MODIFY X switch (op) { case isc_dyn_automap_role: X.RDB$SYSTEM_FLAG = ROLE_FLAG_DBO | ROLE_FLAG_MAY_TRUST; break; case isc_dyn_autounmap_role: X.RDB$SYSTEM_FLAG = ROLE_FLAG_DBO; break; default: DYN_unsupported_verb(); } END_MODIFY; END_FOR; if (!DYN_REQUEST(drq_m_map)) DYN_REQUEST(drq_m_map) = request; if (!found) { status_exception::raise(Arg::Gds(isc_no_meta_update) << Arg::Gds(isc_random) << Arg::Str("Missing RDB$ADMIN role in the database")); } } void get_domain_type(thread_db* tdbb, Global* gbl, dyn_fld& dom_fld) { /************************************** * * g e t _ d o m a i n _ t y p e * ************************************** * * Functional description * Retrieves the type information for a domain so * that it can be compared to a local field before * modifying the datatype of a field. * **************************************/ jrd_req* request = NULL; FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME EQ dom_fld.dyn_fld_source.c_str(); DSC_make_descriptor(&dom_fld.dyn_dsc, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE, FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE, FLD.RDB$CHARACTER_SET_ID, FLD.RDB$COLLATION_ID); dom_fld.dyn_charbytelen = FLD.RDB$FIELD_LENGTH; dom_fld.dyn_dtype = FLD.RDB$FIELD_TYPE; dom_fld.dyn_precision = FLD.RDB$FIELD_PRECISION; dom_fld.dyn_sub_type = FLD.RDB$FIELD_SUB_TYPE; dom_fld.dyn_charlen = FLD.RDB$CHARACTER_LENGTH; dom_fld.dyn_collation = FLD.RDB$COLLATION_ID; dom_fld.dyn_null_flag = FLD.RDB$NULL_FLAG != 0; if (!FLD.RDB$DIMENSIONS.NULL && FLD.RDB$DIMENSIONS > 0) dom_fld.dyn_dtype = blr_blob; END_FOR; CMP_release(tdbb, request); } static ULONG check_update_fld_type(const dyn_fld& orig_fld, const dyn_fld& new_fld) { /************************************** * * c h e c k _ u p d a t e _ f l d _ t y p e * ************************************** * * Functional description * Compare the original field type with the new field type to * determine if the original type can be changed to the new type * * The following conversions are not allowed: * Blob to anything * Array to anything * Date to anything * Char to any numeric * Varchar to any numeric * Anything to Blob * Anything to Array * * This function returns an error code if the conversion can not be * made. If the conversion can be made, FB_SUCCESS is returned **************************************/ // Check to make sure that the old and new types are compatible switch (orig_fld.dyn_dtype) { // CHARACTER types case blr_text: case blr_varying: case blr_cstring: switch (new_fld.dyn_dtype) { case blr_blob: case blr_blob_id: return isc_dyn_dtype_invalid; // Cannot change datatype for column %s. // The operation cannot be performed on BLOB, or ARRAY columns. break; case blr_sql_date: case blr_sql_time: case blr_timestamp: case blr_int64: case blr_long: case blr_short: case blr_d_float: case blr_double: case blr_float: return isc_dyn_dtype_conv_invalid; // Cannot convert column %s from character to non-character data. break; // If the original field is a character field and the new field is a character field, // is there enough space in the new field? case blr_text: case blr_varying: case blr_cstring: { // CVC: Because our caller invoked DSC_make_descriptor() on new_fld previously, // we should have the added bytes for varchar. For cstring, we are done, since // DSC_make_descriptor(DSC_string_length) != DSC_string_length(DSC_make_descriptor). const USHORT maxflen = DSC_string_length(&orig_fld.dyn_dsc); // We can have this assertion since this case is for both string fields. const ULONG new_len = DSC_string_length(&new_fld.dyn_dsc); fb_assert(new_len - maxflen == (ULONG) new_fld.dyn_charbytelen - orig_fld.dyn_charbytelen); // if (new_fld.dyn_dsc.dsc_length < maxflen) if (new_len < maxflen) return isc_dyn_char_fld_too_small; // msg 208: New size specified for column %s must be at least %d characters. } break; default: fb_assert(FALSE); return 87; // MODIFY RDB$FIELDS FAILED } break; // BLOB and ARRAY types case blr_blob: case blr_blob_id: return isc_dyn_dtype_invalid; // Cannot change datatype for column %s. // The operation cannot be performed on BLOB, or ARRAY columns. // DATE types case blr_sql_date: case blr_sql_time: case blr_timestamp: switch (new_fld.dyn_dtype) { case blr_sql_date: if (orig_fld.dyn_dtype == blr_sql_time) return isc_dyn_invalid_dtype_conversion; // Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported. break; case blr_sql_time: if (orig_fld.dyn_dtype == blr_sql_date) return isc_dyn_invalid_dtype_conversion; // Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported. break; case blr_timestamp: if (orig_fld.dyn_dtype == blr_sql_time) return isc_dyn_invalid_dtype_conversion; // Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported. break; // If the original field is a date field and the new field is a character field, // is there enough space in the new field? case blr_text: case blr_text2: case blr_varying: case blr_varying2: case blr_cstring: case blr_cstring2: { const USHORT maxflen = DSC_string_length(&orig_fld.dyn_dsc); // CVC: Solve bug #910423, missing DSC_string_length call. // if (new_fld.dyn_dsc.dsc_length < maxflen) if (DSC_string_length(&new_fld.dyn_dsc) < maxflen) return isc_dyn_char_fld_too_small; // msg 208: New size specified for column %s must be at least %d characters. } break; default: return isc_dyn_invalid_dtype_conversion; // Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported. } break; // NUMERIC types case blr_int64: case blr_long: case blr_short: case blr_d_float: case blr_double: case blr_float: switch (new_fld.dyn_dtype) { case blr_blob: case blr_blob_id: return isc_dyn_dtype_invalid; // Cannot change datatype for column %s. // The operation cannot be performed on BLOB, or ARRAY columns. case blr_sql_date: case blr_sql_time: case blr_timestamp: return isc_dyn_invalid_dtype_conversion; // Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported. // If the original field is a numeric field and the new field is a numeric field, // is there enough space in the new field (do not allow the base type to decrease) case blr_short: switch (orig_fld.dyn_dtype) { case blr_short: return check_update_numeric_type(orig_fld, new_fld); default: return isc_dyn_invalid_dtype_conversion; // Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported. } break; case blr_long: switch (orig_fld.dyn_dtype) { case blr_long: case blr_short: return check_update_numeric_type(orig_fld, new_fld); default: return isc_dyn_invalid_dtype_conversion; // Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported. } break; case blr_float: switch (orig_fld.dyn_dtype) { case blr_float: case blr_short: break; default: return isc_dyn_invalid_dtype_conversion; // Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported. } break; case blr_int64: switch (orig_fld.dyn_dtype) { case blr_int64: case blr_long: case blr_short: return check_update_numeric_type(orig_fld, new_fld); default: return isc_dyn_invalid_dtype_conversion; // Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported. } break; case blr_d_float: case blr_double: switch (orig_fld.dyn_dtype) { case blr_double: case blr_d_float: case blr_float: case blr_short: case blr_long: break; default: return isc_dyn_invalid_dtype_conversion; // Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported. } break; // If the original field is a numeric field and the new field is a character field, // is there enough space in the new field? case blr_text: case blr_varying: case blr_cstring: { const USHORT maxflen = DSC_string_length(&orig_fld.dyn_dsc); // CVC: Solve bug #910423, missing DSC_string_length call. // if (new_fld.dyn_dsc.dsc_length < maxflen) if (DSC_string_length(&new_fld.dyn_dsc) < maxflen) return isc_dyn_char_fld_too_small; // msg 208: New size specified for column %s must be at least %d characters. } break; default: fb_assert(FALSE); return 87; // MODIFY RDB$FIELDS FAILED } break; default: fb_assert(FALSE); return 87; // MODIFY RDB$FIELDS FAILED } return FB_SUCCESS; } static ULONG check_update_numeric_type(const dyn_fld& orig_fld, const dyn_fld& new_fld) { /************************************** * * c h e c k _ u p d a t e _ n u m e r i c _ t y p e * ************************************** * * Functional description * Compare the original field type with the new field type to * determine if the original type can be changed to the new type. * The types should be integral, since it tests only numeric/decimal subtypes * to ensure the scale is not being widened at the expense of the precision, * because the old stored values should fit in the new definition. * * This function returns an error code if the conversion can not be * made. If the conversion can be made, FB_SUCCESS is returned. **************************************/ // Since dsc_scale is negative, the sum of precision and scale produces // the width of the integral part. if (orig_fld.dyn_sub_type && new_fld.dyn_sub_type && orig_fld.dyn_precision + orig_fld.dyn_dsc.dsc_scale > new_fld.dyn_precision + new_fld.dyn_dsc.dsc_scale) { return isc_dyn_scale_too_big; } return FB_SUCCESS; } static void modify_err_punt(thread_db* /*tdbb*/, ULONG errorcode, const dyn_fld& orig_fld_def, const dyn_fld& new_fld_def) { /************************************** * * m o d i f y _ e r r _ p u n t * ************************************** * * Functional description * A generic error routine that calls DYN_error_punt * based on the error code returned by check_update_field_type. * * This function is called by DYN_modify_global_field and by * DYN_modify_sql_field **************************************/ switch (errorcode) { case isc_dyn_dtype_invalid: DYN_error_punt(false, errorcode, orig_fld_def.dyn_fld_name.c_str()); // Cannot change datatype for column %s.The operation cannot be performed on DATE, BLOB, or ARRAY columns. break; case isc_dyn_dtype_conv_invalid: DYN_error_punt(false, errorcode, orig_fld_def.dyn_fld_name.c_str()); // Cannot convert column %s from character to non-character data. break; case isc_dyn_char_fld_too_small: DYN_error_punt(false, errorcode, SafeArg() << orig_fld_def.dyn_fld_name.c_str() << DSC_string_length(&orig_fld_def.dyn_dsc)); // msg 208: New size specified for column %s must be at least %d characters. break; case isc_dyn_scale_too_big: { int code = errorcode; int diff = new_fld_def.dyn_precision - (orig_fld_def.dyn_precision + orig_fld_def.dyn_dsc.dsc_scale); if (diff < 0) { // If new scale becomes negative externally, the message is useless for the user. // (The scale is always zero or negative for us but externally is non-negative.) // Let's ask the user to widen the precision, then. Example: numeric(4, 0) -> numeric(1, 1). code = isc_dyn_precision_too_small; diff = new_fld_def.dyn_precision - new_fld_def.dyn_dsc.dsc_scale - diff; } DYN_error_punt(false, code, SafeArg() << orig_fld_def.dyn_fld_name.c_str() << diff); // scale_too_big: New scale specified for column @1 must be at most @2. // precision_too_small: New precision specified for column @1 must be at least @2. } break; case isc_dyn_invalid_dtype_conversion: { TEXT orig_type[25], new_type[25]; DSC_get_dtype_name(&orig_fld_def.dyn_dsc, orig_type, sizeof(orig_type)); DSC_get_dtype_name(&new_fld_def.dyn_dsc, new_type, sizeof(new_type)); DYN_error_punt(false, errorcode, SafeArg() << orig_fld_def.dyn_fld_name.c_str() << orig_type << new_type); // Cannot change datatype for @1. Conversion from base type @2 to @3 is not supported. } break; default: DYN_error_punt(true, 95); // msg 95: "MODIFY RDB$RELATION_FIELDS failed" } }