mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-25 00:43:03 +01:00
3889 lines
102 KiB
Plaintext
3889 lines
102 KiB
Plaintext
/*
|
|
* PROGRAM: JRD Data Definition Utility
|
|
* MODULE: dyn_mod.epp
|
|
* DESCRIPTION: Dynamic data definition - DYN_modify_<x>
|
|
*
|
|
* 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 <stdio.h>
|
|
#include <string.h>
|
|
|
|
#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";
|
|
|
|
//const int MAX_CHARS_SHORT = 6; // 2**16 = 5 chars + sign
|
|
//const int MAX_CHARS_LONG = 11; // 2**32 = 10 chars + sign
|
|
//const int MAX_CHARS_INT64 = 20; // 2**64 = 19 chars + sign
|
|
//const int MAX_CHARS_DOUBLE = 22; // 15 digits + 2 signs + E + decimal + 3 digit exp
|
|
//const int MAX_CHARS_FLOAT = 13; // 7 digits + 2 signs + E + decimal + 2 digit exp
|
|
|
|
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&);
|
|
|
|
|
|
// ***********************************
|
|
// D Y N _ m o d i f y _ c h a r s e t
|
|
// ***********************************
|
|
// Its purpose is to change the comment in the charset's record.
|
|
void DYN_modify_charset(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_chset, DYN_REQUESTS);
|
|
bool found = false;
|
|
|
|
SqlIdentifier t;
|
|
GET_STRING(ptr, t);
|
|
|
|
try {
|
|
found = false;
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
X IN RDB$CHARACTER_SETS
|
|
WITH X.RDB$CHARACTER_SET_NAME EQ t
|
|
|
|
if (!DYN_REQUEST(drq_m_chset))
|
|
DYN_REQUEST(drq_m_chset) = request;
|
|
|
|
found = true;
|
|
MODIFY X
|
|
UCHAR verb;
|
|
while ((verb = *(*ptr)++) != isc_dyn_end)
|
|
{
|
|
switch (verb)
|
|
{
|
|
case isc_dyn_description:
|
|
if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
|
|
X.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
X.RDB$DESCRIPTION.NULL = TRUE;
|
|
break;
|
|
|
|
default:
|
|
DYN_unsupported_verb();
|
|
}
|
|
}
|
|
END_MODIFY;
|
|
END_FOR;
|
|
|
|
if (!DYN_REQUEST(drq_m_chset))
|
|
DYN_REQUEST(drq_m_chset) = request;
|
|
}
|
|
catch (const Exception& ex) {
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
DYN_rundown_request(request, -1);
|
|
DYN_error_punt(true, 85);
|
|
// msg 85: "MODIFY RDB$CHARACTER_SETS failed"
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
DYN_error_punt(false, 151, t);
|
|
// msg 151: "Character set %s not found"
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************
|
|
// D Y N _ m o d i f y _ c o l l a t i o n
|
|
// ***************************************
|
|
// Its purpose is to change the comment in the collation's record.
|
|
void DYN_modify_collation(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_coll, DYN_REQUESTS);
|
|
bool found = false;
|
|
|
|
SqlIdentifier t;
|
|
GET_STRING(ptr, t);
|
|
|
|
try {
|
|
found = false;
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
X IN RDB$COLLATIONS
|
|
WITH X.RDB$COLLATION_NAME EQ t
|
|
|
|
if (!DYN_REQUEST(drq_m_coll))
|
|
DYN_REQUEST(drq_m_coll) = request;
|
|
|
|
found = true;
|
|
MODIFY X
|
|
UCHAR verb;
|
|
while ((verb = *(*ptr)++) != isc_dyn_end)
|
|
{
|
|
switch (verb)
|
|
{
|
|
case isc_dyn_description:
|
|
if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
|
|
X.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
X.RDB$DESCRIPTION.NULL = TRUE;
|
|
break;
|
|
|
|
default:
|
|
DYN_unsupported_verb();
|
|
}
|
|
}
|
|
END_MODIFY;
|
|
END_FOR;
|
|
|
|
if (!DYN_REQUEST(drq_m_coll))
|
|
DYN_REQUEST(drq_m_coll) = request;
|
|
}
|
|
catch (const Exception& ex) {
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
DYN_rundown_request(request, -1);
|
|
DYN_error_punt(true, 86);
|
|
// msg 86: "MODIFY RDB$COLLATIONS failed"
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
DYN_error_punt(false, 152, t);
|
|
// msg 152: "Collation %s not found"
|
|
}
|
|
}
|
|
|
|
|
|
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_description:
|
|
if (DYN_put_text_blob(gbl, ptr, &DBB.RDB$DESCRIPTION))
|
|
DBB.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
DBB.RDB$DESCRIPTION.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 (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;
|
|
|
|
try {
|
|
|
|
MetaName t;
|
|
GET_STRING(ptr, t);
|
|
|
|
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;
|
|
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;
|
|
|
|
case isc_dyn_description:
|
|
if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
|
|
X.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
X.RDB$DESCRIPTION.NULL = TRUE;
|
|
break;
|
|
|
|
default:
|
|
DYN_unsupported_verb();
|
|
}
|
|
END_MODIFY;
|
|
END_FOR;
|
|
|
|
if (!DYN_REQUEST(drq_m_xcp))
|
|
DYN_REQUEST(drq_m_xcp) = request;
|
|
|
|
}
|
|
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)
|
|
{
|
|
DYN_error_punt(false, 144);
|
|
// msg 144: "Exception not found"
|
|
}
|
|
}
|
|
|
|
|
|
// *********************************
|
|
// D Y N _ m o d i f y _ f i l t e r
|
|
// *********************************
|
|
// Its purpose is to change the comment in the filter's record.
|
|
void DYN_modify_filter(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_bfil, DYN_REQUESTS);
|
|
bool found = false;
|
|
|
|
SqlIdentifier t;
|
|
GET_STRING(ptr, t);
|
|
|
|
try {
|
|
found = false;
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
X IN RDB$FILTERS
|
|
WITH X.RDB$FUNCTION_NAME EQ t
|
|
|
|
if (!DYN_REQUEST(drq_m_bfil))
|
|
DYN_REQUEST(drq_m_bfil) = request;
|
|
|
|
found = true;
|
|
MODIFY X
|
|
UCHAR verb;
|
|
while ((verb = *(*ptr)++) != isc_dyn_end)
|
|
{
|
|
switch (verb)
|
|
{
|
|
case isc_dyn_description:
|
|
if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
|
|
X.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
X.RDB$DESCRIPTION.NULL = TRUE;
|
|
break;
|
|
|
|
// Other cases should go there, like modifying the entry point or module name.
|
|
default:
|
|
DYN_unsupported_verb();
|
|
}
|
|
}
|
|
END_MODIFY;
|
|
END_FOR;
|
|
|
|
if (!DYN_REQUEST(drq_m_bfil))
|
|
DYN_REQUEST(drq_m_bfil) = request;
|
|
}
|
|
catch (const Exception& ex) {
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
DYN_rundown_request(request, -1);
|
|
DYN_error_punt(true, 88);
|
|
// msg 88: "MODIFY RDB$BLOB_FILTERS failed"
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
DYN_error_punt(false, 37, t);
|
|
// msg 37: "Blob Filter %s 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 {
|
|
found = false;
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
X IN RDB$FUNCTIONS
|
|
WITH X.RDB$FUNCTION_NAME EQ t
|
|
|
|
if (!DYN_REQUEST(drq_m_fun))
|
|
DYN_REQUEST(drq_m_fun) = request;
|
|
|
|
found = true;
|
|
MODIFY X
|
|
UCHAR verb;
|
|
while ((verb = *(*ptr)++) != isc_dyn_end)
|
|
{
|
|
switch (verb)
|
|
{
|
|
case isc_dyn_description:
|
|
if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
|
|
X.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
X.RDB$DESCRIPTION.NULL = TRUE;
|
|
break;
|
|
|
|
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)
|
|
{
|
|
DYN_error_punt(false, 41, t);
|
|
// msg 41: "Function %s not found"
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************
|
|
// D Y N _ m o d i f y _ g e n e r a t o r
|
|
// ***************************************
|
|
// Its purpose is to change the comment in the generator's record.
|
|
void DYN_modify_generator(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_gen, DYN_REQUESTS);
|
|
bool found = false;
|
|
SqlIdentifier t;
|
|
GET_STRING(ptr, t);
|
|
|
|
try {
|
|
found = false;
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
X IN RDB$GENERATORS
|
|
WITH X.RDB$GENERATOR_NAME EQ t
|
|
|
|
if (!DYN_REQUEST(drq_m_gen))
|
|
DYN_REQUEST(drq_m_gen) = request;
|
|
|
|
found = true;
|
|
MODIFY X
|
|
UCHAR verb;
|
|
while ((verb = *(*ptr)++) != isc_dyn_end)
|
|
{
|
|
switch (verb)
|
|
{
|
|
case isc_dyn_description:
|
|
if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
|
|
X.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
X.RDB$DESCRIPTION.NULL = TRUE;
|
|
break;
|
|
|
|
default:
|
|
DYN_unsupported_verb();
|
|
}
|
|
}
|
|
END_MODIFY;
|
|
END_FOR;
|
|
|
|
if (!DYN_REQUEST(drq_m_gen))
|
|
DYN_REQUEST(drq_m_gen) = request;
|
|
}
|
|
catch (const Exception& ex) {
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
DYN_rundown_request(request, -1);
|
|
DYN_error_punt(true, 94);
|
|
// msg 94: "MODIFY GENERATOR failed"
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
DYN_error_punt(false, 214, t);
|
|
// msg 214: "Generator %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, bfielddesc,
|
|
bdelvald, bdeldflt, bflddftval, bflddfltsrc;
|
|
bqryname = bqryhdr = bedtstr = bmissingval = false;
|
|
bfldvald = bfldvaldsrc = bfielddesc = bdelvald = bdeldflt = bflddftval = bflddfltsrc = false;
|
|
|
|
const TEXT *qryname, *edtstr;
|
|
const UCHAR *qryhdr, *missingval, *fldvald, *fldvaldsrc, *fielddesc, *flddftval, *flddfltsrc;
|
|
|
|
GET_STRING(ptr, orig_dom.dyn_fld_name);
|
|
|
|
found = false;
|
|
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;
|
|
|
|
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;
|
|
|
|
if (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. */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DYN_error_punt(false, 212);
|
|
/* msg 212: "Zero length identifiers not allowed" */
|
|
}
|
|
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 = (TEXT*) * 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 = (TEXT*) * 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_description:
|
|
fielddesc = *ptr;
|
|
bfielddesc = 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 (bfielddesc) {
|
|
if (DYN_put_text_blob(gbl, &fielddesc, &FLD.RDB$DESCRIPTION))
|
|
FLD.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
FLD.RDB$DESCRIPTION.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;
|
|
|
|
}
|
|
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)
|
|
{
|
|
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);
|
|
|
|
found = false;
|
|
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;
|
|
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;
|
|
|
|
case isc_dyn_description:
|
|
if (DYN_put_text_blob(gbl, ptr, &IDX.RDB$DESCRIPTION))
|
|
IDX.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
IDX.RDB$DESCRIPTION.NULL = TRUE;
|
|
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;
|
|
|
|
}
|
|
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)
|
|
{
|
|
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* description;
|
|
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, dflag, system_flag, scflag, nnflag, ntflag, npflag;
|
|
sfflag = qnflag = qhflag = esflag = dflag = scflag = npflag = nnflag = ntflag = false;
|
|
|
|
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_description:
|
|
description = *ptr;
|
|
dflag = true;
|
|
DYN_skip_attribute(ptr);
|
|
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);
|
|
if (new_fld.length() == 0)
|
|
{
|
|
DYN_error_punt(false, 212);
|
|
/* msg 212: "Zero length identifiers not allowed" */
|
|
}
|
|
|
|
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 (dflag) {
|
|
if (DYN_put_text_blob(gbl, &description, &FLD.RDB$DESCRIPTION))
|
|
FLD.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
FLD.RDB$DESCRIPTION.NULL = TRUE;
|
|
}
|
|
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"
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************
|
|
// D Y N _ m o d i f y _ p a r a m e t e r
|
|
// ***************************************
|
|
// The sole objective of this function is to allow changing the comment
|
|
// in procedure's parameters without going into other changes.
|
|
void DYN_modify_parameter(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_prm, DYN_REQUESTS);
|
|
bool found = false;
|
|
|
|
SqlIdentifier t;
|
|
GET_STRING(ptr, t);
|
|
SqlIdentifier p;
|
|
|
|
try {
|
|
if (**ptr == isc_dyn_prc_name)
|
|
{
|
|
++*ptr;
|
|
GET_STRING(ptr, p);
|
|
}
|
|
else
|
|
DYN_unsupported_verb();
|
|
|
|
found = false;
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
X IN RDB$PROCEDURE_PARAMETERS
|
|
WITH X.RDB$PARAMETER_NAME EQ t
|
|
AND X.RDB$PROCEDURE_NAME EQ p
|
|
|
|
if (!DYN_REQUEST(drq_m_prm))
|
|
DYN_REQUEST(drq_m_prm) = request;
|
|
|
|
found = true;
|
|
MODIFY X
|
|
UCHAR verb;
|
|
while ((verb = *(*ptr)++) != isc_dyn_end)
|
|
{
|
|
switch (verb)
|
|
{
|
|
case isc_dyn_description:
|
|
if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
|
|
X.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
X.RDB$DESCRIPTION.NULL = TRUE;
|
|
break;
|
|
|
|
default:
|
|
DYN_unsupported_verb();
|
|
}
|
|
}
|
|
END_MODIFY;
|
|
END_FOR;
|
|
|
|
if (!DYN_REQUEST(drq_m_prm))
|
|
DYN_REQUEST(drq_m_prm) = request;
|
|
}
|
|
catch (const Exception& ex) {
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
DYN_rundown_request(request, -1);
|
|
DYN_error_punt(true, 100);
|
|
// msg 100: "MODIFY RDB$PROCEDURE_PARAMETERS failed"
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
DYN_error_punt(false, 146, SafeArg() << t << p);
|
|
// msg : "Parameter %s in procedure %s not found"
|
|
}
|
|
}
|
|
|
|
|
|
void DYN_modify_procedure( Global* gbl, const UCHAR** ptr)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ m o d i f y _ p r o c e d u r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Execute a dynamic ddl statement.
|
|
*
|
|
**************************************/
|
|
MetaName procedure_name;
|
|
|
|
GET_STRING(ptr, procedure_name);
|
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
jrd_req* request = NULL;
|
|
bool found = false;
|
|
bool only_description = false;
|
|
|
|
try {
|
|
|
|
request = CMP_find_request(tdbb, drq_m_prcs, DYN_REQUESTS);
|
|
|
|
found = false;
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_NAME = procedure_name.c_str()
|
|
|
|
if (!DYN_REQUEST(drq_m_prcs))
|
|
DYN_REQUEST(drq_m_prcs) = request;
|
|
|
|
found = true;
|
|
|
|
/* Set NULL flags to TRUE only for fields which must be specified in the DYN string.
|
|
Retain existing values on other fields (RDB$DESCRIPTION, RDB$SECURITY_CLASS),
|
|
unless explicitly changed in the DYN string */
|
|
MODIFY P
|
|
|
|
if (**ptr == isc_dyn_description)
|
|
{
|
|
++*ptr;
|
|
if (DYN_put_text_blob(gbl, ptr, &P.RDB$DESCRIPTION))
|
|
P.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
P.RDB$DESCRIPTION.NULL = TRUE;
|
|
|
|
if (**ptr == isc_dyn_end)
|
|
only_description = true;
|
|
}
|
|
|
|
if (!only_description)
|
|
{
|
|
P.RDB$SYSTEM_FLAG = 0;
|
|
P.RDB$SYSTEM_FLAG.NULL = FALSE;
|
|
P.RDB$PROCEDURE_BLR.NULL = TRUE;
|
|
P.RDB$PROCEDURE_SOURCE.NULL = TRUE;
|
|
P.RDB$PROCEDURE_INPUTS.NULL = TRUE;
|
|
P.RDB$PROCEDURE_OUTPUTS.NULL = TRUE;
|
|
|
|
// ODS_11_1 fields
|
|
P.RDB$DEBUG_INFO.NULL = TRUE;
|
|
|
|
P.RDB$PROCEDURE_TYPE = prc_legacy;
|
|
P.RDB$PROCEDURE_TYPE.NULL = FALSE;
|
|
|
|
P.RDB$VALID_BLR = TRUE;
|
|
P.RDB$VALID_BLR.NULL = FALSE;
|
|
}
|
|
|
|
UCHAR verb;
|
|
while ((verb = *(*ptr)++) != isc_dyn_end)
|
|
switch (verb)
|
|
{
|
|
case isc_dyn_system_flag:
|
|
P.RDB$SYSTEM_FLAG = DYN_get_number(ptr);
|
|
P.RDB$SYSTEM_FLAG.NULL = FALSE;
|
|
break;
|
|
|
|
case isc_dyn_prc_blr:
|
|
if (DYN_put_blr_blob(gbl, ptr, &P.RDB$PROCEDURE_BLR))
|
|
P.RDB$PROCEDURE_BLR.NULL = FALSE;
|
|
else
|
|
P.RDB$PROCEDURE_BLR.NULL = TRUE;
|
|
break;
|
|
|
|
case isc_dyn_description:
|
|
if (DYN_put_text_blob(gbl, ptr, &P.RDB$DESCRIPTION))
|
|
P.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
P.RDB$DESCRIPTION.NULL = TRUE;
|
|
break;
|
|
|
|
case isc_dyn_prc_source:
|
|
if (DYN_put_text_blob(gbl, ptr, &P.RDB$PROCEDURE_SOURCE))
|
|
P.RDB$PROCEDURE_SOURCE.NULL = FALSE;
|
|
else
|
|
P.RDB$PROCEDURE_SOURCE.NULL = TRUE;
|
|
break;
|
|
|
|
case isc_dyn_prc_inputs:
|
|
P.RDB$PROCEDURE_INPUTS = DYN_get_number(ptr);
|
|
P.RDB$PROCEDURE_INPUTS.NULL = FALSE;
|
|
break;
|
|
|
|
case isc_dyn_prc_outputs:
|
|
P.RDB$PROCEDURE_OUTPUTS = DYN_get_number(ptr);
|
|
P.RDB$PROCEDURE_OUTPUTS.NULL = FALSE;
|
|
break;
|
|
|
|
case isc_dyn_security_class:
|
|
GET_STRING(ptr, P.RDB$SECURITY_CLASS);
|
|
P.RDB$SECURITY_CLASS.NULL = FALSE;
|
|
break;
|
|
|
|
case isc_dyn_debug_info:
|
|
DYN_put_blr_blob(gbl, ptr, &P.RDB$DEBUG_INFO); // ODS_11_1 field
|
|
P.RDB$DEBUG_INFO.NULL = FALSE;
|
|
break;
|
|
|
|
case isc_dyn_prc_type:
|
|
P.RDB$PROCEDURE_TYPE = DYN_get_number(ptr); // ODS_11_1 field
|
|
break;
|
|
|
|
default:
|
|
--(*ptr);
|
|
DYN_execute(gbl, ptr, NULL, NULL, NULL, NULL, &procedure_name);
|
|
}
|
|
|
|
END_MODIFY;
|
|
END_FOR;
|
|
|
|
if (!DYN_REQUEST(drq_m_prcs))
|
|
DYN_REQUEST(drq_m_prcs) = request;
|
|
}
|
|
catch (const Exception& ex) {
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
DYN_rundown_request(request, -1);
|
|
DYN_error_punt(true, 141);
|
|
/* msg 141: "MODIFY RDB$PROCEDURES failed" */
|
|
}
|
|
|
|
if (!found) {
|
|
DYN_error_punt(false, 140, procedure_name.c_str());
|
|
/* msg 140: "Procedure %s not found" */
|
|
}
|
|
}
|
|
|
|
|
|
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 {
|
|
|
|
found = false;
|
|
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);
|
|
|
|
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;
|
|
|
|
case isc_dyn_description:
|
|
if (DYN_put_text_blob(gbl, ptr, &REL.RDB$DESCRIPTION))
|
|
REL.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
REL.RDB$DESCRIPTION.NULL = TRUE;
|
|
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;
|
|
|
|
}
|
|
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)
|
|
{
|
|
DYN_error_punt(false, 101);
|
|
/* msg 101: "Relation field not found" */
|
|
}
|
|
}
|
|
|
|
|
|
// *****************************
|
|
// D Y N _ m o d i f y _ r o l e
|
|
// *****************************
|
|
// Its purpose is to change the comment in the role's record.
|
|
void DYN_modify_role(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_rol, DYN_REQUESTS);
|
|
bool found = false;
|
|
SqlIdentifier t;
|
|
GET_STRING(ptr, t);
|
|
|
|
try {
|
|
found = false;
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
X IN RDB$ROLES
|
|
WITH X.RDB$ROLE_NAME EQ t
|
|
|
|
if (!DYN_REQUEST(drq_m_rol))
|
|
DYN_REQUEST(drq_m_rol) = request;
|
|
|
|
found = true;
|
|
MODIFY X
|
|
UCHAR verb;
|
|
while ((verb = *(*ptr)++) != isc_dyn_end)
|
|
{
|
|
switch (verb)
|
|
{
|
|
case isc_dyn_description:
|
|
if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
|
|
X.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
X.RDB$DESCRIPTION.NULL = TRUE;
|
|
break;
|
|
|
|
default:
|
|
DYN_unsupported_verb();
|
|
}
|
|
}
|
|
END_MODIFY;
|
|
END_FOR;
|
|
|
|
if (!DYN_REQUEST(drq_m_rol))
|
|
DYN_REQUEST(drq_m_rol) = request;
|
|
}
|
|
catch (const Exception& ex) {
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
DYN_rundown_request(request, -1);
|
|
DYN_error_punt(true, 158);
|
|
// msg 158: "MODIFY RDB$ROLES failed"
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
DYN_error_punt(false, 155, t);
|
|
// msg 155: "Role %s not found"
|
|
}
|
|
}
|
|
|
|
|
|
void DYN_modify_trigger( Global* gbl, const UCHAR** ptr)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ m o d i f y _ t r i g g e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Modify a trigger for a relation.
|
|
*
|
|
**************************************/
|
|
MetaName trigger_name;
|
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, drq_m_trigger, DYN_REQUESTS);
|
|
bool found = false;
|
|
bool only_description = false;
|
|
|
|
try {
|
|
|
|
GET_STRING(ptr, trigger_name);
|
|
const UCHAR* source = NULL;
|
|
const UCHAR* blr = NULL;
|
|
const UCHAR* debug_info_ptr = NULL;
|
|
|
|
found = false;
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
X IN RDB$TRIGGERS WITH X.RDB$TRIGGER_NAME EQ trigger_name.c_str()
|
|
|
|
if (!DYN_REQUEST(drq_m_trigger)) {
|
|
DYN_REQUEST(drq_m_trigger) = request;
|
|
}
|
|
|
|
if (X.RDB$RELATION_NAME.NULL && !tdbb->getAttachment()->locksmith())
|
|
ERR_post(Arg::Gds(isc_adm_task_denied));
|
|
|
|
// CVC: I think that we'll do well by hiding our automatic triggers from this function.
|
|
// Why would a user want to fiddle with triggers that were generated automatically?
|
|
|
|
if (!X.RDB$SYSTEM_FLAG.NULL)
|
|
{
|
|
switch (X.RDB$SYSTEM_FLAG)
|
|
{
|
|
case fb_sysflag_check_constraint:
|
|
case fb_sysflag_referential_constraint:
|
|
case fb_sysflag_view_check:
|
|
DYN_error_punt(false, 109);
|
|
// msg 109: "Triggers created automatically cannot be modified"
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
found = true;
|
|
|
|
MODIFY X
|
|
|
|
if (**ptr == isc_dyn_description)
|
|
{
|
|
++*ptr;
|
|
if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
|
|
X.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
X.RDB$DESCRIPTION.NULL = TRUE;
|
|
|
|
if (**ptr == isc_dyn_end)
|
|
only_description = true;
|
|
}
|
|
|
|
UCHAR verb;
|
|
while ((verb = *(*ptr)++) != isc_dyn_end)
|
|
switch (verb)
|
|
{
|
|
case isc_dyn_trg_name:
|
|
{
|
|
MetaName new_trigger_name;
|
|
GET_STRING(ptr, new_trigger_name);
|
|
if (new_trigger_name.length() == 0)
|
|
DYN_error_punt(false, 212);
|
|
// msg 212: "Zero length identifiers not allowed"
|
|
strcpy (X.RDB$TRIGGER_NAME, new_trigger_name.c_str());
|
|
}
|
|
break;
|
|
|
|
case isc_dyn_trg_type:
|
|
X.RDB$TRIGGER_TYPE = DYN_get_number(ptr);
|
|
X.RDB$TRIGGER_TYPE.NULL = FALSE;
|
|
break;
|
|
|
|
case isc_dyn_trg_sequence:
|
|
X.RDB$TRIGGER_SEQUENCE = DYN_get_number(ptr);
|
|
X.RDB$TRIGGER_SEQUENCE.NULL = FALSE;
|
|
break;
|
|
|
|
case isc_dyn_trg_inactive:
|
|
X.RDB$TRIGGER_INACTIVE = DYN_get_number(ptr);
|
|
X.RDB$TRIGGER_INACTIVE.NULL = FALSE;
|
|
break;
|
|
|
|
case isc_dyn_rel_name:
|
|
GET_STRING(ptr, X.RDB$RELATION_NAME);
|
|
X.RDB$RELATION_NAME.NULL = FALSE;
|
|
break;
|
|
|
|
case isc_dyn_trg_blr:
|
|
blr = *ptr;
|
|
DYN_skip_attribute(ptr);
|
|
if (DYN_put_blr_blob(gbl, &blr, &X.RDB$TRIGGER_BLR))
|
|
X.RDB$TRIGGER_BLR.NULL = FALSE;
|
|
else
|
|
X.RDB$TRIGGER_BLR.NULL = TRUE;
|
|
break;
|
|
|
|
case isc_dyn_trg_source:
|
|
source = *ptr;
|
|
DYN_skip_attribute(ptr);
|
|
if (DYN_put_text_blob(gbl, &source, &X.RDB$TRIGGER_SOURCE))
|
|
X.RDB$TRIGGER_SOURCE.NULL = FALSE;
|
|
else
|
|
X.RDB$TRIGGER_SOURCE.NULL = TRUE;
|
|
break;
|
|
|
|
case isc_dyn_description:
|
|
if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
|
|
X.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
X.RDB$DESCRIPTION.NULL = TRUE;
|
|
break;
|
|
|
|
case isc_dyn_debug_info:
|
|
debug_info_ptr = *ptr;
|
|
DYN_skip_attribute(ptr);
|
|
break;
|
|
|
|
default:
|
|
--(*ptr);
|
|
DYN_execute(gbl, ptr, NULL, NULL, &trigger_name, NULL, NULL);
|
|
}
|
|
END_MODIFY;
|
|
END_FOR;
|
|
|
|
if (!DYN_REQUEST(drq_m_trigger))
|
|
DYN_REQUEST(drq_m_trigger) = request;
|
|
|
|
if (!only_description)
|
|
{
|
|
if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_1)
|
|
{
|
|
jrd_req* sub_request = NULL;
|
|
|
|
FOR(REQUEST_HANDLE sub_request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
TRG IN RDB$TRIGGERS WITH TRG.RDB$TRIGGER_NAME EQ trigger_name.c_str()
|
|
|
|
MODIFY TRG USING
|
|
TRG.RDB$VALID_BLR = TRUE;
|
|
TRG.RDB$VALID_BLR.NULL = FALSE;
|
|
|
|
TRG.RDB$DEBUG_INFO.NULL = (debug_info_ptr == NULL) ? TRUE : FALSE;
|
|
if (debug_info_ptr)
|
|
DYN_put_blr_blob(gbl, &debug_info_ptr, &TRG.RDB$DEBUG_INFO);
|
|
END_MODIFY;
|
|
END_FOR;
|
|
|
|
CMP_release(tdbb, sub_request);
|
|
}
|
|
}
|
|
|
|
}
|
|
catch (const Exception& ex) {
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
DYN_rundown_request(request, -1);
|
|
DYN_error_punt(true, 102);
|
|
/* msg 102: "MODIFY TRIGGER failed" */
|
|
}
|
|
|
|
if (!found) {
|
|
DYN_error_punt(false, 147, trigger_name.c_str());
|
|
/* msg 147: "Trigger %s not found" */
|
|
}
|
|
}
|
|
|
|
|
|
void DYN_modify_trigger_msg( Global* gbl, const UCHAR** ptr, MetaName* trigger_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ m o d i f y _ t r i g g e r _ m s g
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Modify a trigger message.
|
|
*
|
|
**************************************/
|
|
MetaName t;
|
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, drq_m_trg_msg, DYN_REQUESTS);
|
|
|
|
try {
|
|
|
|
const int number = DYN_get_number(ptr);
|
|
if (trigger_name)
|
|
t = *trigger_name;
|
|
else if (*(*ptr)++ == isc_dyn_trg_name)
|
|
GET_STRING(ptr, t);
|
|
else
|
|
DYN_error_punt(false, 103);
|
|
/* msg 103: "TRIGGER NAME expected" */
|
|
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
X IN RDB$TRIGGER_MESSAGES
|
|
WITH X.RDB$MESSAGE_NUMBER EQ number AND X.RDB$TRIGGER_NAME EQ t.c_str()
|
|
|
|
if (!DYN_REQUEST(drq_m_trg_msg))
|
|
DYN_REQUEST(drq_m_trg_msg) = request;
|
|
|
|
MODIFY X
|
|
UCHAR verb;
|
|
while ((verb = *(*ptr)++) != isc_dyn_end)
|
|
switch (verb)
|
|
{
|
|
case isc_dyn_trg_msg_number:
|
|
X.RDB$MESSAGE_NUMBER = DYN_get_number(ptr);
|
|
X.RDB$MESSAGE_NUMBER.NULL = FALSE;
|
|
break;
|
|
|
|
case isc_dyn_trg_msg:
|
|
GET_STRING(ptr, X.RDB$MESSAGE);
|
|
X.RDB$MESSAGE.NULL = FALSE;
|
|
break;
|
|
|
|
default:
|
|
DYN_unsupported_verb();
|
|
}
|
|
END_MODIFY;
|
|
END_FOR;
|
|
|
|
if (!DYN_REQUEST(drq_m_trg_msg))
|
|
DYN_REQUEST(drq_m_trg_msg) = request;
|
|
|
|
}
|
|
catch (const Exception& ex) {
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
DYN_rundown_request(request, -1);
|
|
DYN_error_punt(true, 105);
|
|
/* msg 105: "MODIFY TRIGGER MESSAGE failed" */
|
|
}
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
if (view_name.length() == 0)
|
|
{
|
|
DYN_error_punt(false, 212);
|
|
/* msg 212: "Zero length identifiers not allowed" */
|
|
}
|
|
|
|
jrd_req* request = NULL;
|
|
bool found = false;
|
|
|
|
try {
|
|
|
|
request = CMP_find_request(tdbb, drq_m_view, DYN_REQUESTS);
|
|
|
|
found = false;
|
|
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;
|
|
bool only_description = false;
|
|
|
|
MODIFY REL
|
|
|
|
if (**ptr == isc_dyn_description)
|
|
{
|
|
++*ptr;
|
|
if (DYN_put_text_blob(gbl, ptr, &REL.RDB$DESCRIPTION))
|
|
REL.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
REL.RDB$DESCRIPTION.NULL = TRUE;
|
|
|
|
if (**ptr == isc_dyn_end)
|
|
only_description = true;
|
|
}
|
|
|
|
if (!only_description)
|
|
{
|
|
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);
|
|
}
|
|
|
|
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;
|
|
|
|
case isc_dyn_description:
|
|
if (DYN_put_text_blob(gbl, ptr, &REL.RDB$DESCRIPTION))
|
|
REL.RDB$DESCRIPTION.NULL = FALSE;
|
|
else
|
|
REL.RDB$DESCRIPTION.NULL = TRUE;
|
|
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;
|
|
|
|
}
|
|
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) {
|
|
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_s_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_s_difference))
|
|
{
|
|
DYN_REQUEST(drq_s_difference) = request;
|
|
}
|
|
}
|
|
catch (const Exception& ex) {
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
DYN_rundown_request(request, drq_s_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();
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
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;
|
|
|
|
if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_2)
|
|
{
|
|
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;
|
|
|
|
if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_2)
|
|
{
|
|
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" */
|
|
}
|
|
}
|