8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 12:43:03 +01:00

Added check of relation temporary scope for foreign key constraints defined

in CREATE TABLE statement. Noted by Adriano
This commit is contained in:
hvlad 2006-05-23 20:01:17 +00:00
parent ed5d99b595
commit caeacdc13d
2 changed files with 150 additions and 66 deletions

View File

@ -194,7 +194,8 @@ const int drq_m_view = 143; // modify view
const int drq_s_colls = 144; /* store collations */
const int drq_l_colls = 145; /* lookup collations */
const int drq_dom_is_array = 146; // lookup domain to see if it's an array
const int drq_l_rel_info = 147; // lookup relations flags
const int drq_MAX = 148;
const int drq_l_rel_info = 147; // lookup name and flags of one master relation
const int drq_l_rel_info2 = 148; // lookup names and flags of all master relations
const int drq_MAX = 149;
#endif /* JRD_DRQ_H */

View File

@ -121,6 +121,9 @@ static bool get_who(thread_db*, Global*, Firebird::MetaName&);
static bool is_it_user_name(Global*, const Firebird::MetaName&, thread_db*);
static USHORT skip_blr_blob(const UCHAR** ptr);
static void check_foreign_key_temp_scope(thread_db*, Global*, const TEXT*, const TEXT*);
static void check_relation_temp_scope(thread_db*, Global*, const TEXT*, const SSHORT);
static void make_relation_scope_name(const TEXT*, const SSHORT, Firebird::string&);
void DYN_define_collation( Global* gbl, const UCHAR** ptr)
{
@ -352,66 +355,6 @@ void DYN_define_collation( Global* gbl, const UCHAR** ptr)
}
static void check_fk_against_temp(thread_db* tdbb, Global* gbl,
const TEXT* child_rel_name, const TEXT* master_index_name)
{
Database* dbb = tdbb->tdbb_database;
const SSHORT id = drq_l_rel_info;
jrd_req* request = CMP_find_request(tdbb, drq_l_rel_info, DYN_REQUESTS);
bool bErr = false;
char sMaster[256], sChild[256];
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
RLC_M IN RDB$RELATION_CONSTRAINTS CROSS
REL_C IN RDB$RELATIONS CROSS
REL_M IN RDB$RELATIONS
WITH (RLC_M.RDB$CONSTRAINT_TYPE EQ UNIQUE_CNSTRT OR
RLC_M.RDB$CONSTRAINT_TYPE EQ PRIMARY_KEY)
AND RLC_M.RDB$INDEX_NAME EQ master_index_name
AND REL_C.RDB$RELATION_NAME EQ child_rel_name
AND REL_M.RDB$RELATION_NAME EQ RLC_M.RDB$RELATION_NAME
bErr = ( (REL_M.RDB$FLAGS & REL_IS_GLOBAL_TEMP) !=
(REL_C.RDB$FLAGS & REL_IS_GLOBAL_TEMP) ) &&
!( (REL_M.RDB$FLAGS & REL_global_temp_preserve) &&
(REL_C.RDB$FLAGS & REL_global_temp_delete) );
if (bErr)
{
fb_utils::exact_name_limit(REL_M.RDB$RELATION_NAME, sizeof(REL_M.RDB$RELATION_NAME));
fb_utils::exact_name_limit(REL_C.RDB$RELATION_NAME, sizeof(REL_C.RDB$RELATION_NAME));
char *scope = NULL;
if (REL_M.RDB$FLAGS & REL_global_temp_preserve)
scope = "global temporary table \"%s\" of type on commit preserve rows";
else if (REL_M.RDB$FLAGS & REL_global_temp_delete)
scope = "global temporary table \"%s\" of type on commit delete rows";
else
scope = "persistent table \"%s\"";
snprintf(sMaster, sizeof(sMaster)-1, scope, REL_M.RDB$RELATION_NAME);
if (REL_C.RDB$FLAGS & REL_global_temp_preserve)
scope = "global temporary table \"%s\" of type on commit preserve rows";
else if (REL_C.RDB$FLAGS & REL_global_temp_delete)
scope = "global temporary table \"%s\" of type on commit delete rows";
else
scope = "persistent table \"%s\"";
snprintf(sChild, sizeof(sChild)-1, scope, REL_C.RDB$RELATION_NAME);
}
END_FOR;
if (!DYN_REQUEST(drq_l_rel_info))
DYN_REQUEST(drq_l_rel_info) = request;
if (bErr) {
DYN_error_punt(false, 232, // Msg 232 : "%s can't reference %s"
sChild, sMaster, NULL, NULL, NULL);
}
}
void DYN_define_constraint(Global* gbl,
const UCHAR** ptr,
const Firebird::MetaName* relation_name,
@ -503,7 +446,7 @@ void DYN_define_constraint(Global* gbl,
strcpy(CRT.RDB$INDEX_NAME, index_name.c_str());
CRT.RDB$INDEX_NAME.NULL = FALSE;
check_fk_against_temp(tdbb, gbl,
check_foreign_key_temp_scope(tdbb, gbl,
relation_name->c_str(), referred_index_name.c_str());
/* check that we have references permissions on the table and
@ -527,14 +470,14 @@ void DYN_define_constraint(Global* gbl,
// msg 124: "A column name is repeated in the definition of constraint: %s"
// msg 125: "Integrity constraint lookup failed"
// msg 127: "STORE RDB$REF_CONSTRAINTS failed"
// msg 231: "%s cannot reference %s"
// msg 232: "%s cannot reference %s"
switch (id) {
case drq_s_rel_con: number = 121; local_id = id; break;
case drq_s_ref_con: number = 127; local_id = id; break;
case drq_c_unq_nam: number = 121; break;
case drq_n_idx_seg: number = 124; break;
case drq_c_dup_con: number = 125; break;
case drq_l_rel_info: number = 231; break;
case drq_l_rel_info: number = 232; break;
default: number = 125; break;
}
@ -542,7 +485,7 @@ void DYN_define_constraint(Global* gbl,
DYN_error_punt(true,
number,
(number == 124 || number == 231) ? constraint_name.c_str() : NULL,
number == 124 ? constraint_name.c_str() : NULL,
NULL,
NULL,
NULL,
@ -3246,6 +3189,10 @@ void DYN_define_relation( Global* gbl, const UCHAR** ptr)
DYN_execute(gbl, ptr, &tmp, &field_name, NULL, NULL, NULL);
}
check_relation_temp_scope(tdbb, gbl,
REL.RDB$RELATION_NAME,
REL.RDB$FLAGS);
if (sql_prot) {
if (!get_who(tdbb, gbl, owner_name))
DYN_error_punt(true, 115, NULL, NULL, NULL, NULL, NULL);
@ -4599,3 +4546,139 @@ static USHORT skip_blr_blob(const UCHAR** ptr)
return length;
}
static void check_foreign_key_temp_scope(thread_db* tdbb, Global* gbl,
const TEXT* child_rel_name, const TEXT* master_index_name)
{
/**********************************************************
*
* c h e c k _ f o r e i g n _ k e y _ t e m p _ s c o p e
*
**********************************************************
*
* Functional description
* Check temporary table reference rules between given child
* relation and master relation (owner of given PK\UK index)
*
**********************************************************/
Database* dbb = tdbb->tdbb_database;
const SSHORT id = drq_l_rel_info;
jrd_req* request = CMP_find_request(tdbb, drq_l_rel_info, DYN_REQUESTS);
bool bErr = false;
Firebird::string sMaster, sChild;
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
RLC_M IN RDB$RELATION_CONSTRAINTS CROSS
REL_C IN RDB$RELATIONS CROSS
REL_M IN RDB$RELATIONS
WITH (RLC_M.RDB$CONSTRAINT_TYPE EQ UNIQUE_CNSTRT OR
RLC_M.RDB$CONSTRAINT_TYPE EQ PRIMARY_KEY)
AND RLC_M.RDB$INDEX_NAME EQ master_index_name
AND REL_C.RDB$RELATION_NAME EQ child_rel_name
AND REL_M.RDB$RELATION_NAME EQ RLC_M.RDB$RELATION_NAME
bErr = ( (REL_M.RDB$FLAGS & REL_IS_GLOBAL_TEMP) !=
(REL_C.RDB$FLAGS & REL_IS_GLOBAL_TEMP) ) &&
!( (REL_M.RDB$FLAGS & REL_global_temp_preserve) &&
(REL_C.RDB$FLAGS & REL_global_temp_delete) );
if (bErr)
{
fb_utils::exact_name_limit(REL_M.RDB$RELATION_NAME, sizeof(REL_M.RDB$RELATION_NAME));
fb_utils::exact_name_limit(REL_C.RDB$RELATION_NAME, sizeof(REL_C.RDB$RELATION_NAME));
make_relation_scope_name(REL_M.RDB$RELATION_NAME, REL_M.RDB$FLAGS, sMaster);
make_relation_scope_name(REL_C.RDB$RELATION_NAME, REL_C.RDB$FLAGS, sChild);
}
END_FOR;
if (!DYN_REQUEST(drq_l_rel_info))
DYN_REQUEST(drq_l_rel_info) = request;
if (bErr) {
DYN_error_punt(false, 232, // Msg 232 : "%s can't reference %s"
sChild.c_str(), sMaster.c_str(), NULL, NULL, NULL);
}
}
static void check_relation_temp_scope(thread_db* tdbb, Global* gbl,
const TEXT* child_rel_name, const SSHORT child_rel_flags)
{
/****************************************************
*
* c h e c k _ r e l a t i o n _ t e m p _ s c o p e
*
****************************************************
*
* Functional description
* Check temporary table reference rules between just
* created child relation and all its master relations
*
****************************************************/
Database* dbb = tdbb->tdbb_database;
const SSHORT id = drq_l_rel_info2;
jrd_req* request = CMP_find_request(tdbb, drq_l_rel_info2, DYN_REQUESTS);
bool bErr = false;
Firebird::string sMaster, sChild;
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
RLC_C IN RDB$RELATION_CONSTRAINTS CROSS
IND_C IN RDB$INDICES CROSS
IND_M IN RDB$INDICES CROSS
REL_M IN RDB$RELATIONS
WITH RLC_C.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY
AND RLC_C.RDB$RELATION_NAME EQ child_rel_name
AND IND_C.RDB$INDEX_NAME EQ RLC_C.RDB$INDEX_NAME
AND IND_M.RDB$INDEX_NAME EQ IND_C.RDB$FOREIGN_KEY
AND IND_M.RDB$RELATION_NAME EQ REL_M.RDB$RELATION_NAME
bErr = ( (REL_M.RDB$FLAGS & REL_IS_GLOBAL_TEMP) !=
(child_rel_flags & REL_IS_GLOBAL_TEMP) ) &&
!( (REL_M.RDB$FLAGS & REL_global_temp_preserve) &&
(child_rel_flags & REL_global_temp_delete) );
if (bErr)
{
fb_utils::exact_name_limit(REL_M.RDB$RELATION_NAME, sizeof(REL_M.RDB$RELATION_NAME));
make_relation_scope_name(REL_M.RDB$RELATION_NAME, REL_M.RDB$FLAGS, sMaster);
make_relation_scope_name(child_rel_name, child_rel_flags, sChild);
}
END_FOR;
if (!DYN_REQUEST(drq_l_rel_info2))
DYN_REQUEST(drq_l_rel_info2) = request;
if (bErr) {
DYN_error_punt(false, 232, // Msg 232 : "%s can't reference %s"
sChild.c_str(), sMaster.c_str(), NULL, NULL, NULL);
}
}
static void make_relation_scope_name(const TEXT* rel_name, const SSHORT rel_flags,
Firebird::string& str)
{
/**************************************************
*
* m a k e _ r e l a t i o n _ s c o p e _ n a m e
*
**************************************************
*
* Functional description
* Make string with relation name and type
* of its temporary scope
*
**************************************************/
char *scope = NULL;
if (rel_flags & REL_global_temp_preserve)
scope = "global temporary table \"%s\" of type on commit preserve rows";
else if (rel_flags & REL_global_temp_delete)
scope = "global temporary table \"%s\" of type on commit delete rows";
else
scope = "persistent table \"%s\"";
str.printf(scope, rel_name);
}