8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-27 04:43:02 +01:00
firebird-mirror/src/jrd/dyn_mod.epp

2998 lines
79 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 "../jrd/ib_stdio.h"
#include <string.h>
#include "../jrd/common.h"
#include <stdarg.h>
#include "../jrd/jrd.h"
#include "../jrd/tra.h"
#include "../jrd/scl.h"
#include "../jrd/drq.h"
#include "../jrd/flags.h"
#include "../jrd/y_ref.h"
#include "../jrd/ibase.h"
#include "../jrd/lls.h"
#include "../jrd/all.h"
#include "../jrd/met.h"
#include "../jrd/btr.h"
#include "../jrd/intl.h"
#include "../jrd/dyn.h"
#include "../jrd/ods.h"
#include "../jrd/all_proto.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/thd_proto.h"
#include "../jrd/vio_proto.h"
#include "../jrd/dsc_proto.h"
#include "../jrd/ail.h"
#ifdef SUPERSERVER
#define V4_THREADING
#endif
DATABASE DB = STATIC "ODS.RDB";
#define MAX_CHARS_SHORT 6 /* 2**16 = 5 chars + sign */
#define MAX_CHARS_LONG 11 /* 2**32 = 10 chars + sign */
#define MAX_CHARS_INT64 20 /* 2**64 = 19 chars + sign */
#define MAX_CHARS_DOUBLE 22 /* 15 digits + 2 signs + E + decimal + 3 digit exp */
#define 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 drop_cache(GBL);
static void change_backup_mode(GBL, UCHAR verb);
static void drop_log(GBL);
// Function not defined in this file MOD 04-July-2002
// static void modify_lfield_type(GBL, const UCHAR**, TEXT*, TEXT*);
static void modify_lfield_position(TDBB, DBB, GBL, const TEXT*, const TEXT*,
USHORT, USHORT);
static bool check_view_dependency(TDBB, DBB, GBL, const TEXT*, const TEXT*);
static bool check_sptrig_dependency(TDBB, DBB, GBL, const TEXT*, const TEXT*);
static void modify_lfield_index(TDBB, DBB, GBL, const TEXT*, const TEXT*,
const TEXT*);
static bool field_exists(TDBB, DBB, GBL, const TEXT*, const TEXT*);
static bool domain_exists(TDBB, DBB, GBL, const TEXT*);
static void get_domain_type(TDBB, DBB, GBL, DYN_FLD);
static ULONG check_update_fld_type(const dyn_fld*, const dyn_fld*);
static void modify_err_punt(TDBB, ULONG, const dyn_fld*, const dyn_fld*);
void DYN_modify_database( GBL 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 verb, s[128];
SSHORT num_log_buffers = 0;
USHORT log_buffer_size = 0;
SLONG check_point_len = 0, group_commit_wait = -1;
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
BLK request = NULL;
try {
INF_database_info(reinterpret_cast<const SCHAR*>(alloc_info), sizeof(alloc_info),
reinterpret_cast<SCHAR*>(s), sizeof(s));
if (s[0] != isc_info_allocation) {
goto dyn_punt_84;
}
request = (BLK) 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);
bool log_params_defined = false;
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;
#ifdef SUPERSERVER
bool first_log_file = true;
#endif
MODIFY DBB USING
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, 158);
break;
case isc_dyn_def_difference:
DYN_define_difference(gbl, ptr);
break;
#ifdef SUPERSERVER
case isc_dyn_def_default_log:
DYN_define_log_file(gbl, ptr, first_log_file, true);
break;
case isc_dyn_def_log_file:
DYN_define_log_file(gbl, ptr, first_log_file, false);
first_log_file = false;
break;
#endif
case isc_dyn_def_cache_file:
DYN_define_cache(gbl, ptr);
break;
case isc_dyn_log_group_commit_wait:
group_commit_wait = DYN_get_number(ptr);
log_params_defined = true;
break;
case isc_dyn_log_buffer_size:
log_buffer_size = DYN_get_number(ptr);
log_params_defined = true;
break;
case isc_dyn_log_check_point_length:
check_point_len = DYN_get_number(ptr);
log_params_defined = true;
break;
case isc_dyn_log_num_of_buffers:
num_log_buffers = DYN_get_number(ptr);
log_params_defined = true;
break;
case isc_dyn_drop_cache:
drop_cache(gbl);
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_drop_log:
drop_log(gbl);
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;
default:
--(*ptr);
DYN_execute(gbl, ptr, NULL, NULL, NULL, NULL, NULL);
}
END_MODIFY;
END_FOR;
if (log_params_defined)
AIL_set_log_options(check_point_len, num_log_buffers, log_buffer_size,
group_commit_wait);
if (!DYN_REQUEST(drq_m_database))
DYN_REQUEST(drq_m_database) = request;
}
catch (const std::exception&) {
DYN_rundown_request(request, -1);
DYN_error_punt(true, 84, NULL, NULL, NULL, NULL, NULL);
/* msg 84: "MODIFY DATABASE failed" */
}
return;
dyn_punt_84:
DYN_error_punt(true, 84, NULL, NULL, NULL, NULL, NULL);
/* msg 84: "MODIFY DATABASE failed" */
}
void DYN_modify_exception( GBL 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.
*
**************************************/
UCHAR verb;
SqlIdentifier t;
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
BLK request = (BLK) CMP_find_request(tdbb, drq_m_xcp, DYN_REQUESTS);
bool found = false;
try {
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
if (!DYN_REQUEST(drq_m_xcp)) DYN_REQUEST(drq_m_xcp) = request;
found = true;
MODIFY X
while ((verb = *(*ptr)++) != isc_dyn_end)
switch (verb) {
case isc_dyn_xcp_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_xcp))
DYN_REQUEST(drq_m_xcp) = request;
}
catch (const std::exception&) {
DYN_rundown_request(request, -1);
DYN_error_punt(true, 145, NULL, NULL, NULL, NULL, NULL);
/* msg 145: "MODIFY EXCEPTION failed" */
}
if (!found)
{
DYN_error_punt(false, 144, NULL, NULL, NULL, NULL, NULL);
/* msg 144: "Exception not found" */
}
}
void DYN_modify_global_field(
GBL gbl,
const UCHAR** ptr,
const TEXT* relation_name, TEXT* 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.
*
**************************************/
BLK old_request;
UCHAR verb;
SSHORT field_adjusted_count = 0;
const TEXT* err_one_type_change_only =
"Only one data type change to the domain allowed at a time";
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
BLK request = (BLK) CMP_find_request(tdbb, drq_m_gfield, DYN_REQUESTS);
bool found = false;
dyn_fld* orig_dom = 0;
dyn_fld* new_dom = 0;
try {
/* Allocate the field structures */
orig_dom = (DYN_FLD) gds__alloc(sizeof(struct dyn_fld));
if (!orig_dom) {
DYN_error_punt(true, 211, NULL, NULL, NULL, NULL, NULL);
}
MOVE_CLEAR(orig_dom, sizeof(struct dyn_fld));
new_dom = (DYN_FLD) gds__alloc(sizeof(struct dyn_fld));
if (!new_dom) {
DYN_error_punt(true, 211, NULL, NULL, NULL, NULL, NULL);
}
MOVE_CLEAR(new_dom, sizeof(struct dyn_fld));
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;
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME EQ orig_dom->dyn_fld_name
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_charlen = FLD.RDB$CHARACTER_LENGTH;
orig_dom->dyn_collation = FLD.RDB$COLLATION_ID;
orig_dom->dyn_null_flag = FLD.RDB$NULL_FLAG;
/* 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;
while ((verb = *(*ptr)++) != isc_dyn_end)
{
switch (verb)
{
case isc_dyn_fld_name:
{
SqlIdentifier newfld;
if (GET_STRING(ptr, newfld))
{
if (!domain_exists(tdbb, dbb, gbl, newfld))
{
MODIFY FLD USING
strcpy(FLD.RDB$FIELD_NAME, newfld);
FLD.RDB$FIELD_NAME.NULL = FALSE;
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
MODIFY DIM_DOM USING
strcpy (DIM_DOM.RDB$FIELD_NAME, newfld);
DIM_DOM.RDB$FIELD_NAME.NULL = FALSE;
END_MODIFY;
END_FOR;
CMP_release (tdbb, (JRD_REQ) 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
MODIFY DOM USING
strcpy(DOM.RDB$FIELD_SOURCE, newfld);
DOM.RDB$FIELD_SOURCE.NULL = FALSE;
END_MODIFY;
modify_lfield_index(tdbb, dbb, gbl,
DOM.RDB$RELATION_NAME,
DOM.RDB$FIELD_NAME,
DOM.RDB$FIELD_NAME);
END_FOR;
CMP_release(tdbb, (JRD_REQ)request);
request = old_request;
END_MODIFY;
}
else
{
DYN_error_punt(false, 204, orig_dom->dyn_fld_name,
newfld, NULL, NULL, NULL);
/* msg 204: Cannot rename domain %s to %s. A domain with that name already exists. */
}
}
else
{
DYN_error_punt(false, 212, NULL, NULL, NULL, NULL, NULL);
/* 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, (JRD_REQ)request);
DYN_error_punt(false, 5, err_one_type_change_only, NULL, NULL, NULL, NULL);
}
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, (JRD_REQ)request);
DYN_error_punt(false, 5, err_one_type_change_only, NULL, NULL, NULL, NULL);
}
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_dsc.dsc_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, (JRD_REQ)request);
DYN_error_punt(false, 160, NULL, NULL, NULL, NULL, NULL);
/* msg 160: "Only one constraint allowed for a domain" */
break;
}
else
single_validate = true;
break;
case isc_dyn_fld_validation_blr:
if (single_validate && (!FLD.RDB$VALIDATION_BLR.NULL)) {
EXE_unwind(tdbb, (JRD_REQ)request);
DYN_error_punt(false, 160, NULL, NULL, NULL, NULL, NULL);
/* msg 160: "Only one constraint allowed for a domain" */
break;
}
else
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:
flddftval = *ptr;
bflddftval = true;
DYN_skip_attribute(ptr);
break;
case isc_dyn_fld_default_source:
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_dsc.dsc_sub_type,
new_dom->dyn_charset, new_dom->dyn_collation);
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 */
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
modify_lfield_index(tdbb, dbb, gbl,
DOM.
RDB$RELATION_NAME,
DOM.RDB$FIELD_NAME,
DOM.RDB$FIELD_NAME);
END_FOR;
CMP_release(tdbb, (JRD_REQ)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_dsc.dsc_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 (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) {
FLD.RDB$DEFAULT_VALUE.NULL = FALSE;
DYN_put_blr_blob(gbl, &flddftval, &FLD.RDB$DEFAULT_VALUE);
}
if (bflddfltsrc) {
FLD.RDB$DEFAULT_SOURCE.NULL = FALSE;
DYN_put_text_blob(gbl, &flddfltsrc, &FLD.RDB$DEFAULT_SOURCE);
}
END_MODIFY;
END_FOR;
if (!DYN_REQUEST(drq_m_gfield))
DYN_REQUEST(drq_m_gfield) = request;
if (orig_dom)
gds__free(orig_dom);
if (new_dom)
gds__free(new_dom);
}
catch (const std::exception&) {
if (orig_dom) {
gds__free(orig_dom);
}
if (new_dom) {
gds__free(new_dom);
}
DYN_rundown_request(request, -1);
DYN_error_punt(true, 87, NULL, NULL, NULL, NULL, NULL);
/* msg 87: "MODIFY RDB$FIELDS failed" */
}
if (!found)
{
DYN_error_punt(false, 89, NULL, NULL, NULL, NULL, NULL);
/* msg 89: "Global field not found" */
}
}
void DYN_modify_index( GBL gbl, const UCHAR** ptr)
{
/**************************************
*
* D Y N _ m o d i f y _ i n d e x
*
**************************************
*
* Functional description
* Modify an existing index
*
**************************************/
UCHAR verb;
SqlIdentifier name;
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
BLK request = (BLK) 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
if (!DYN_REQUEST(drq_m_index)) DYN_REQUEST(drq_m_index) = request;
found = true;
MODIFY IDX USING
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 std::exception&) {
DYN_rundown_request(request, -1);
DYN_error_punt(true, 91, NULL, NULL, NULL, NULL, NULL);
/* msg 91: "MODIFY RDB$INDICES failed" */
}
if (!found)
{
DYN_error_punt(false, 93, NULL, NULL, NULL, NULL, NULL);
/* msg 93: "Index field not found" */
}
}
void DYN_modify_local_field(
GBL gbl,
const UCHAR** ptr,
const TEXT* relation_name, TEXT* field_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, existing_position;
UCHAR verb;
SqlIdentifier f, r;
TEXT *query_name, *edit_string,
*security_class, *new_name;
const UCHAR *query_header, *description;
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
GET_STRING(ptr, f);
r[0] = 0;
bool sfflag, qnflag, qhflag, esflag, dflag, system_flag, scflag, nnflag,
ntflag, npflag;
sfflag = qnflag = qhflag = esflag = dflag = scflag = npflag = nnflag =
ntflag = false;
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 = (TEXT*) * ptr;
nnflag = true;
DYN_skip_attribute(ptr);
break;
case isc_dyn_fld_query_name:
query_name = (TEXT*) * 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 = (TEXT*) * ptr;
esflag = true;
DYN_skip_attribute(ptr);
break;
case isc_dyn_security_class:
security_class = (TEXT*) * ptr;
scflag = true;
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, field_name, NULL, NULL, NULL);
}
BLK request = (BLK) CMP_find_request(tdbb, drq_m_lfield, DYN_REQUESTS);
bool found = false;
try {
found = false;
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
FLD IN RDB$RELATION_FIELDS
WITH FLD.RDB$FIELD_NAME EQ f AND FLD.RDB$RELATION_NAME EQ r
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) {
SqlIdentifier new_fld;
new_fld[0] = 0;
GET_STRING(&new_name, new_fld);
MET_exact_name(new_fld);
if (!new_fld[0])
DYN_error_punt(false, 212, NULL, NULL, NULL, NULL, NULL);
/* msg 212: "Zero length identifiers not allowed" */
check_view_dependency(tdbb, dbb, gbl, r, f);
check_sptrig_dependency(tdbb, dbb, gbl, r, f);
if (!field_exists(tdbb, dbb, gbl, r, new_fld)) {
strcpy(FLD.RDB$FIELD_NAME, new_fld);
modify_lfield_index(tdbb, dbb, gbl, r, f,
FLD.RDB$FIELD_NAME);
}
else {
DYN_error_punt(false, 205, f, new_fld, r, NULL, NULL);
/* 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 (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, dbb, gbl, r, f, position,
existing_position);
}
catch (const std::exception&) {
DYN_rundown_request(request, -1);
DYN_error_punt(true, 95, NULL, NULL, NULL, NULL, NULL);
/* msg 95: "MODIFY RDB$RELATION_FIELDS failed" */
}
if (!found)
{
DYN_error_punt(false, 96, NULL, NULL, NULL, NULL, NULL);
/* msg 96: "Local column not found" */
}
}
void DYN_modify_procedure( GBL 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.
*
**************************************/
UCHAR verb;
SqlIdentifier procedure_name;
GET_STRING(ptr, procedure_name);
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
BLK request = NULL;
bool found = false;
try {
request = (BLK) 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
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
P.RDB$SYSTEM_FLAG.NULL = TRUE;
P.RDB$PROCEDURE_BLR.NULL = TRUE;
P.RDB$PROCEDURE_SOURCE.NULL = TRUE;
P.RDB$PROCEDURE_INPUTS.NULL = TRUE;
P.RDB$PROCEDURE_OUTPUTS.NULL = TRUE;
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:
P.RDB$PROCEDURE_BLR.NULL = FALSE;
DYN_put_blr_blob(gbl, ptr, &P.RDB$PROCEDURE_BLR);
break;
case isc_dyn_description:
DYN_put_text_blob(gbl, ptr, &P.RDB$DESCRIPTION);
P.RDB$DESCRIPTION.NULL = FALSE;
break;
case isc_dyn_prc_source:
DYN_put_text_blob(gbl, ptr, &P.RDB$PROCEDURE_SOURCE);
P.RDB$PROCEDURE_SOURCE.NULL = FALSE;
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;
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 std::exception&) {
DYN_rundown_request(request, -1);
DYN_error_punt(true, 141, NULL, NULL, NULL, NULL, NULL);
/* msg 141: "MODIFY RDB$PROCEDURES failed" */
}
if (!found) {
DYN_error_punt(false, 140, procedure_name, NULL, NULL, NULL, NULL);
/* msg 140: "Procedure %s not found" */
}
}
void DYN_modify_relation( GBL 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
*
**************************************/
UCHAR verb;
SqlIdentifier name, field_name;
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
field_name[0] = 0;
GET_STRING(ptr, name);
BLK request = (BLK) 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
if (!DYN_REQUEST(drq_m_relation))
DYN_REQUEST(drq_m_relation) = request;
if (!REL.RDB$VIEW_BLR.NULL)
DYN_error_punt(false, 177, NULL, NULL, NULL, NULL, NULL);
found = true;
MODIFY REL USING
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, NULL, NULL, NULL, NULL,
NULL);
/* 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, NULL, NULL, NULL, NULL,
NULL);
/* 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 std::exception&) {
DYN_rundown_request(request, -1);
DYN_error_punt(true, 99, NULL, NULL, NULL, NULL, NULL);
/* msg 99: "MODIFY RDB$RELATIONS failed" */
}
if (!found)
{
DYN_error_punt(false, 101, NULL, NULL, NULL, NULL, NULL);
/* msg 101: "Relation field not found" */
}
}
void DYN_modify_trigger( GBL 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.
*
**************************************/
UCHAR verb;
SqlIdentifier trigger_name;
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
BLK request = (BLK) CMP_find_request(tdbb, drq_m_trigger, DYN_REQUESTS);
bool found = false;
try {
GET_STRING(ptr, trigger_name);
const UCHAR* source = NULL;
const UCHAR* blr = NULL;
found = false;
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
X IN RDB$TRIGGERS WITH X.RDB$TRIGGER_NAME EQ trigger_name
if (!DYN_REQUEST(drq_m_trigger)) {
DYN_REQUEST(drq_m_trigger) = request;
}
/* 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 frb_sysflag_check_constraint:
case frb_sysflag_referential_constraint:
case frb_sysflag_view_check:
continue;
default:
break;
}
}
*/
found = true;
MODIFY X
while ((verb = *(*ptr)++) != isc_dyn_end)
switch (verb) {
case isc_dyn_trg_name:
{
SqlIdentifier new_trigger_name;
new_trigger_name[0] = 0;
/* GET_STRING(ptr, X.RDB$TRIGGER_NAME); */
GET_STRING(ptr, new_trigger_name);
MET_exact_name(new_trigger_name);
if (!new_trigger_name[0])
DYN_error_punt(false, 212, NULL, NULL, NULL, NULL, NULL);
/* msg 212: "Zero length identifiers not allowed" */
strcpy (X.RDB$TRIGGER_NAME, new_trigger_name);
}
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);
DYN_put_blr_blob(gbl, &blr, &X.RDB$TRIGGER_BLR);
X.RDB$TRIGGER_BLR.NULL = FALSE;
break;
case isc_dyn_trg_source:
source = *ptr;
DYN_skip_attribute(ptr);
DYN_put_text_blob(gbl, &source, &X.RDB$TRIGGER_SOURCE);
X.RDB$TRIGGER_SOURCE.NULL = FALSE;
break;
case isc_dyn_description:
DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION);
X.RDB$DESCRIPTION.NULL = FALSE;
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;
}
catch (const std::exception&) {
DYN_rundown_request(request, -1);
DYN_error_punt(true, 102, NULL, NULL, NULL, NULL, NULL);
/* msg 102: "MODIFY TRIGGER failed" */
}
if (!found) {
DYN_error_punt(false, 147, trigger_name, NULL, NULL, NULL, NULL);
/* msg 147: "Trigger %s not found" */
}
}
void DYN_modify_trigger_msg( GBL gbl, const UCHAR** ptr, TEXT* 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.
*
**************************************/
int number;
UCHAR verb;
SqlIdentifier t;
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
BLK request = (BLK) CMP_find_request(tdbb, drq_m_trg_msg, DYN_REQUESTS);
try {
number = DYN_get_number(ptr);
if (trigger_name)
strcpy(t, trigger_name);
else if (*(*ptr)++ == isc_dyn_trg_name)
GET_STRING(ptr, t);
else
DYN_error_punt(false, 103, NULL, NULL, NULL, NULL, NULL);
/* 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
if (!DYN_REQUEST(drq_m_trg_msg))
DYN_REQUEST(drq_m_trg_msg) = request;
MODIFY X
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 std::exception&) {
DYN_rundown_request(request, -1);
DYN_error_punt(true, 105, NULL, NULL, NULL, NULL, NULL);
/* msg 105: "MODIFY TRIGGER MESSAGE failed" */
}
}
void DYN_modify_view( GBL gbl, const UCHAR** ptr)
{
/**************************************
*
* D Y N _ m o d i f y _ v i e w
*
**************************************
*
* Functional description
* Execute a dynamic ddl statement.
*
**************************************/
UCHAR verb;
SqlIdentifier view_name;
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
view_name[0] = 0;
GET_STRING(ptr, view_name);
MET_exact_name(view_name);
if (!view_name[0])
DYN_error_punt(false, 212, NULL, NULL, NULL, NULL, NULL);
/* msg 212: "Zero length identifiers not allowed" */
BLK request = NULL;
bool found = false;
try {
request = (BLK) CMP_find_request(tdbb, drq_m_relation, DYN_REQUESTS);
found = false;
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
VRL IN RDB$VIEW_RELATIONS CROSS REL IN RDB$RELATIONS
WITH VRL.RDB$VIEW_NAME EQ REL.RDB$RELATION_NAME AND
VRL.RDB$VIEW_NAME EQ view_name
if (!DYN_REQUEST(drq_m_relation))
DYN_REQUEST(drq_m_relation) = request;
found = true;
MODIFY REL
REL.RDB$SYSTEM_FLAG.NULL = TRUE;
REL.RDB$VIEW_BLR.NULL = TRUE;
REL.RDB$VIEW_SOURCE.NULL = TRUE;
REL.RDB$SECURITY_CLASS.NULL = TRUE;
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:
DYN_put_blr_blob(gbl, ptr, &REL.RDB$VIEW_BLR);
REL.RDB$VIEW_BLR.NULL = FALSE;
break;
case isc_dyn_view_source:
DYN_put_text_blob(gbl, ptr, &REL.RDB$VIEW_SOURCE);
REL.RDB$VIEW_SOURCE.NULL = FALSE;
break;
case isc_dyn_security_class:
GET_STRING(ptr, REL.RDB$SECURITY_CLASS);
REL.RDB$SECURITY_CLASS.NULL = FALSE;
break;
default:
--(*ptr);
DYN_execute(gbl, ptr, REL.RDB$RELATION_NAME, NULL,
NULL, NULL, NULL);
}
END_MODIFY;
END_FOR;
if (!DYN_REQUEST(drq_m_relation))
DYN_REQUEST(drq_m_relation) = request;
}
catch (const std::exception&) {
DYN_rundown_request(request, -1);
DYN_error_punt(true, 99, NULL, NULL, NULL, NULL, NULL);
/* msg 99: "MODIFY RDB$RELATIONS failed" */
}
if (!found) {
DYN_error_punt(false, 61, NULL, NULL, NULL, NULL, NULL);
/* msg 61: "Relation not found" */
}
}
static void drop_cache( GBL gbl)
{
/**************************************
*
* d r o p _ c a c h e
*
**************************************
*
* Functional description
* Drop the database cache
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
BLK request = (BLK) CMP_find_request(tdbb, drq_d_cache, DYN_REQUESTS);
bool found = false;
try {
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
X IN RDB$FILES WITH X.RDB$FILE_FLAGS EQ FILE_cache
ERASE X;
found = true;
END_FOR;
if (!DYN_REQUEST(drq_d_cache)) {
DYN_REQUEST(drq_d_cache) = request;
}
}
catch (const std::exception&) {
DYN_rundown_request(request, drq_d_cache);
DYN_error_punt(true, 63, NULL, NULL, NULL, NULL, NULL);
/* msg 63: ERASE RDB$FILE failed */
}
if (!found)
{
DYN_error_punt(false, 149, NULL, NULL, NULL, NULL, NULL);
/* msg 149: "Shared cache file not found" */
}
}
static void change_backup_mode( GBL 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;
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
BLK request = (BLK) 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 std::exception&) {
DYN_rundown_request(request, drq_d_difference);
DYN_error_punt(true, 63, NULL, NULL, NULL, NULL, NULL);
/* msg 63: ERASE RDB$FILE failed */
}
if (!found && verb == isc_dyn_begin_backup) try {
request = (BLK) 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 std::exception&) {
DYN_rundown_request(request, drq_s_difference);
DYN_error_punt(true, 150, NULL, NULL, NULL, NULL, NULL);
/* msg 150: STORE RDB$FILES failed */
}
if (invalid_state) {
DYN_error_punt(false, verb == isc_dyn_begin_backup ? 217 : 218,
NULL, NULL, NULL, NULL, NULL);
/* 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,
NULL, NULL, NULL, NULL, NULL);
/* msg 218: "Database is not in the physical backup mode" */
/* msg 215: "Difference file is not defined" */
}
}
static void drop_log( GBL gbl)
{
/**************************************
*
* d r o p _ l o g
*
**************************************
*
* Functional description
* Delete all log files
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
BLK request = (BLK) CMP_find_request(tdbb, drq_d_log, DYN_REQUESTS);
bool found = false;
try {
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
X IN RDB$LOG_FILES
ERASE X;
found = true;
END_FOR;
if (!DYN_REQUEST(drq_d_log))
DYN_REQUEST(drq_d_log) = request;
}
catch (const std::exception&) {
DYN_rundown_request(request, drq_d_log);
DYN_error_punt(true, 153, NULL, NULL, NULL, NULL, NULL);
/* msg 153: ERASE RDB$LOG_FILE failed */
}
if (!found)
{
DYN_error_punt(false, 152, NULL, NULL, NULL, NULL, NULL);
/* msg 152: "Write ahead log not found" */
}
}
static void modify_lfield_position(TDBB tdbb,
DBB dbb,
GBL gbl,
const TEXT* relation_name,
const TEXT* 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
*
***********************************************************/
volatile BLK 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 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
*/
MET_exact_name(FLD.RDB$FIELD_NAME);
if (strcmp(FLD.RDB$FIELD_NAME, field_name) == 0) {
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, (JRD_REQ)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
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, (JRD_REQ)request);
}
catch (const std::exception&) {
DYN_error_punt(true, 95, NULL, NULL, NULL, NULL, NULL);
/* msg 95: "MODIFY RDB$RELATION_FIELDS failed" */
}
}
static bool check_view_dependency(TDBB tdbb,
DBB dbb,
GBL gbl,
const TEXT* relation_name,
const TEXT* 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.
*
***********************************************************/
BLK request = NULL;
bool retval = false;
const TEXT* view_name = NULL;
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 AND
X.RDB$FIELD_NAME EQ field_name 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, (JRD_REQ)request);
if (retval)
DYN_error_punt(false, 206, field_name, relation_name, view_name, NULL,
NULL);
/* msg 206: Column %s from table %s is referenced in %s. */
return retval;
}
static bool check_sptrig_dependency(TDBB tdbb,
DBB dbb,
GBL gbl,
const TEXT* relation_name,
const TEXT* 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 refereneced, return true, else return
* false, but true causes the function to punt instead.
***********************************************************/
BLK request = NULL;
bool retval = false;
const TEXT* dep_name = NULL;
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
FIRST 1
DEP IN RDB$DEPENDENCIES WITH
DEP.RDB$DEPENDED_ON_NAME EQ relation_name AND
DEP.RDB$FIELD_NAME EQ field_name
retval = true;
dep_name = DEP.RDB$DEPENDENT_NAME;
END_FOR;
CMP_release(tdbb, (JRD_REQ)request);
if (retval)
DYN_error_punt(false, 206, field_name, relation_name, dep_name, NULL,
NULL);
/* msg 206: Column %s from table %s is referenced in %s. */
return retval;
}
static void modify_lfield_index(TDBB tdbb,
DBB dbb,
GBL gbl,
const TEXT* relation_name,
const TEXT* field_name,
const TEXT* 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
*
***********************************************************/
BLK 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 AND
IDXS.RDB$FIELD_NAME EQ field_name
/* Change the name of the field in the index */
MODIFY IDXS USING
MOVE_FASTER(new_fld_name, IDXS.RDB$FIELD_NAME,
sizeof(IDXS.RDB$FIELD_NAME));
END_MODIFY;
/* Set the index name to itself to tell the index to rebuild */
MODIFY IDX USING
MOVE_FASTER(IDX.RDB$INDEX_NAME, IDX.RDB$INDEX_NAME,
sizeof(IDX.RDB$INDEX_NAME));
END_MODIFY;
END_FOR;
CMP_release(tdbb, (JRD_REQ)request);
}
static bool field_exists(TDBB tdbb,
DBB dbb,
GBL gbl,
const TEXT* relation_name,
const TEXT* 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
***********************************************************/
BLK 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 AND
FLD.RDB$FIELD_NAME EQ field_name
retval = true;
END_FOR;
CMP_release(tdbb, (JRD_REQ)request);
return retval;
}
static bool domain_exists(TDBB tdbb, DBB dbb, GBL gbl, const TEXT* 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
***********************************************************/
BLK 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
retval = true;
END_FOR;
CMP_release(tdbb, (JRD_REQ)request);
return retval;
}
void DYN_modify_sql_field(GBL gbl,
const UCHAR** ptr,
const TEXT* relation_name,
TEXT* field_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.
*
**************************************/
UCHAR verb;
SSHORT field_adjusted_count = 0;
const TEXT* err_one_type_change_only =
"Only one data type change to the field allowed at a time";
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
dyn_fld* orig_fld = 0;
dyn_fld* new_fld = 0;
dyn_fld* dom_fld = 0;
try {
/* Allocate the field structures */
orig_fld = (DYN_FLD) gds__alloc(sizeof(struct dyn_fld));
if (!orig_fld)
DYN_error_punt(false, 211, NULL, NULL, NULL, NULL, NULL);
MOVE_CLEAR(orig_fld, sizeof(struct dyn_fld));
new_fld = (DYN_FLD) gds__alloc(sizeof(struct dyn_fld));
if (!new_fld)
DYN_error_punt(false, 211, NULL, NULL, NULL, NULL, NULL);
MOVE_CLEAR(new_fld, sizeof(struct dyn_fld));
dom_fld = (DYN_FLD) gds__alloc(sizeof(struct dyn_fld));
if (!dom_fld)
DYN_error_punt(false, 211, NULL, NULL, NULL, NULL, NULL);
MOVE_CLEAR(dom_fld, sizeof(struct dyn_fld));
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).
*/
check_sptrig_dependency(tdbb, dbb, gbl, relation_name,
orig_fld->dyn_fld_name);
BLK request = NULL;
BLK 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;
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
RFR IN RDB$RELATION_FIELDS WITH RFR.RDB$RELATION_NAME = relation_name
AND RFR.RDB$FIELD_NAME = orig_fld->dyn_fld_name
first_request = request;
request = NULL;
found = true;
bool has_dimensions = false;
bool update_domain = 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_charlen = FLD.RDB$CHARACTER_LENGTH;
orig_fld->dyn_collation = FLD.RDB$COLLATION_ID;
orig_fld->dyn_null_flag = FLD.RDB$NULL_FLAG;
strcpy(orig_fld->dyn_fld_source, RFR.RDB$FIELD_SOURCE);
/* 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;
}
while ((verb = *(*ptr)++) != isc_dyn_end) {
switch (verb) {
case isc_dyn_fld_source:
GET_STRING(ptr, dom_fld->dyn_fld_source);
get_domain_type(tdbb, dbb, 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, 5, err_one_type_change_only, NULL, NULL, NULL, NULL);
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, 5, err_one_type_change_only, NULL, NULL, NULL, NULL);
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_dsc.dsc_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;
/* 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);
DYN_execute(gbl, ptr, relation_name, RFR.RDB$FIELD_SOURCE,
NULL, NULL, NULL);
}
}
END_FOR; /* FLD in RDB$FIELDS */
CMP_release(tdbb, (JRD_REQ)request);
request = NULL;
/* Now that we have all of the information needed, let's check to see if the field type can be modifed */
if (update_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_dsc.dsc_sub_type,
dom_fld->dyn_charset, dom_fld->dyn_collation);
*/
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 (!strncmp(orig_fld->dyn_fld_source, IMPLICIT_DOMAIN_PREFIX,
IMPLICIT_DOMAIN_PREFIX_LEN))
{
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, (JRD_REQ)request);
request = NULL;
}
request = first_request;
MODIFY RFR USING
strcpy(RFR.RDB$FIELD_SOURCE, dom_fld->dyn_fld_source);
RFR.RDB$FIELD_SOURCE.NULL = FALSE;
END_MODIFY;
first_request = request;
}
else {
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_dsc.dsc_sub_type,
new_fld->dyn_charset, new_fld->dyn_collation);
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 (strncmp(orig_fld->dyn_fld_source, IMPLICIT_DOMAIN_PREFIX,
IMPLICIT_DOMAIN_PREFIX_LEN))
{
request = first_request;
MODIFY RFR USING
DYN_UTIL_generate_field_name(tdbb, gbl,
RFR.RDB$FIELD_SOURCE);
strcpy(new_fld->dyn_fld_name, 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.NULL = TRUE;
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_dsc.dsc_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 (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;
}
/* Copy the field name into RDB$FIELDS */
strcpy(FLD.RDB$FIELD_NAME, new_fld->dyn_fld_name);
END_STORE;
CMP_release(tdbb, (JRD_REQ)request);
request = NULL;
}
else { /* the original and new definitions are both base types */
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_dsc.dsc_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 (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;
}
END_MODIFY;
END_FOR; /* FLD in RDB$FIELDS */
CMP_release(tdbb, (JRD_REQ)request);
request = NULL;
}
} /* else not a domain */
request = first_request;
END_FOR; /* RFR IN RDB$RELATION_FIELDS */
CMP_release(tdbb, (JRD_REQ)request);
request = NULL;
if (!found)
DYN_error_punt(false, 96, NULL, NULL, NULL, NULL, NULL);
/* msg 96: "Local column not found" */
/* Update any indicies that exist */
modify_lfield_index(tdbb, dbb, gbl, relation_name, orig_fld->dyn_fld_name,
orig_fld->dyn_fld_name);
if (new_fld)
gds__free(new_fld);
if (dom_fld)
gds__free(dom_fld);
if (orig_fld)
gds__free(orig_fld);
}
catch (const std::exception&) {
if (new_fld) {
gds__free(new_fld);
}
if (dom_fld) {
gds__free(dom_fld);
}
if (orig_fld) {
gds__free(orig_fld);
}
DYN_error_punt(true, 95, NULL, NULL, NULL, NULL, NULL);
/* msg 95: "MODIFY RDB$RELATION_FIELDS failed" */
}
}
void get_domain_type(TDBB tdbb, DBB dbb, GBL 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.
*
**************************************/
BLK 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;
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_charlen = FLD.RDB$CHARACTER_LENGTH;
dom_fld->dyn_collation = FLD.RDB$COLLATION_ID;
dom_fld->dyn_null_flag = FLD.RDB$NULL_FLAG;
END_FOR;
CMP_release(tdbb, (JRD_REQ)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 used, since
DSC_make_descriptor(DSC_string_length) != DSC_string_length(DSC_make_descriptor). */
USHORT maxflen = DSC_string_length(&orig_fld->dyn_dsc);
/* We can have this assertion since this case is for both string fields. */
fb_assert(DSC_string_length(&new_fld->dyn_dsc) - maxflen
== new_fld->dyn_charbytelen - orig_fld->dyn_charbytelen);
/* if (new_fld->dyn_dsc.dsc_length < maxflen) */
if (DSC_string_length(&new_fld->dyn_dsc) < maxflen)
return isc_dyn_char_fld_too_small;
/* New size specified for column %s must be greater than %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. */
break;
/* 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:
{
USHORT maxflen = DSC_string_length(&orig_fld->dyn_dsc);
if (new_fld->dyn_dsc.dsc_length < maxflen)
return isc_dyn_char_fld_too_small;
/* New size specified for column %s must be greater than %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. */
break;
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:
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_long:
switch (orig_fld->dyn_dtype) {
case blr_long:
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_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:
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_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:
{
USHORT maxflen = DSC_string_length(&orig_fld->dyn_dsc);
if (new_fld->dyn_dsc.dsc_length < maxflen)
return isc_dyn_char_fld_too_small;
/* New size specified for column %s must be greater than %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 void modify_err_punt(TDBB 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, 207, orig_fld_def->dyn_fld_name, NULL, NULL,
NULL, NULL);
/* 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, 210, orig_fld_def->dyn_fld_name, NULL, NULL,
NULL, NULL);
/* Cannot convert column %s from character to non-character data. */
break;
case isc_dyn_char_fld_too_small:
/* TMN: Bad, bad, bad cast. int -> TEXT* */
DYN_error_punt(false,
208,
orig_fld_def->dyn_fld_name,
(TEXT*)DSC_string_length(&orig_fld_def->dyn_dsc),
NULL,
NULL,
NULL);
/* msg 208: New size specified for column %s must be greater than %d characters. */
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, 209, orig_fld_def->dyn_fld_name, orig_type,
new_type, NULL, NULL);
/* Cannot convert base type %s to base type %s. */
break;
}
default:
DYN_error_punt(true, 95, NULL, NULL, NULL, NULL, NULL);
/* msg 95: "MODIFY RDB$RELATION_FIELDS failed" */
}
}