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

Postfix for #8062 - CREATE IF NOT EXISTS.

Thanks to Pavel Zotov.
This commit is contained in:
Adriano dos Santos Fernandes 2024-04-15 23:20:30 -03:00
parent 4e80dde604
commit 003b2e0a77
3 changed files with 85 additions and 56 deletions

View File

@ -675,12 +675,16 @@ already exists.
For ALTER TABLE ... ADD subclause, DDL triggers are not fired if there are only IF NOT EXISTS subclauses and all
of them are related to existing columns or constraints.
For others commands where IF NOT EXISTS is part of the main command, DDL triggers are not fired when the object
already exists.
For others commands (except users currently) where IF NOT EXISTS is part of the main command,
DDL triggers are not fired when the object already exists.
The engine only verifies if the name (object, column or constraint) already exists, and if yes, do nothing.
It never tries to match the existing object with the one being created.
Some objects share the same "namespace", for example, there cannot be a table and a procedure with the same name.
In this case, if there is table XYZ and CREATE PROCEDURE IF NOT EXISTS XYZ is tried, the procedure will not be created
and no error will be raised.
The following statements are supported:
CREATE EXCEPTION [IF NOT EXISTS] ...

View File

@ -1754,8 +1754,8 @@ void CreateAlterFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsql
status_exception::raise(Arg::Gds(isc_dyn_func_not_found) << Arg::Str(name));
}
}
else
executeCreate(tdbb, dsqlScratch, transaction);
else if (!executeCreate(tdbb, dsqlScratch, transaction))
return;
compile(tdbb, dsqlScratch);
@ -1781,7 +1781,7 @@ void CreateAlterFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsql
}
}
void CreateAlterFunctionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
bool CreateAlterFunctionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
jrd_tra* transaction)
{
Attachment* const attachment = transaction->getAttachment();
@ -1790,7 +1790,7 @@ void CreateAlterFunctionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch
if (package.isEmpty())
{
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_udf))
return;
return false;
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE,
DDL_TRIGGER_CREATE_FUNCTION, name, NULL);
@ -1865,6 +1865,8 @@ void CreateAlterFunctionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch
storePrivileges(tdbb, transaction, name, obj_udf, EXEC_PRIVILEGES);
executeAlter(tdbb, dsqlScratch, transaction, false, false);
return true;
}
bool CreateAlterFunctionNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
@ -2772,8 +2774,8 @@ void CreateAlterProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsq
status_exception::raise(Arg::Gds(isc_dyn_proc_not_found) << Arg::Str(name));
}
}
else
executeCreate(tdbb, dsqlScratch, transaction);
else if (!executeCreate(tdbb, dsqlScratch, transaction))
return;
compile(tdbb, dsqlScratch);
@ -2799,7 +2801,7 @@ void CreateAlterProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsq
}
}
void CreateAlterProcedureNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
bool CreateAlterProcedureNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
jrd_tra* transaction)
{
Attachment* const attachment = transaction->getAttachment();
@ -2808,7 +2810,7 @@ void CreateAlterProcedureNode::executeCreate(thread_db* tdbb, DsqlCompilerScratc
if (package.isEmpty())
{
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_procedure))
return;
return false;
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE,
DDL_TRIGGER_CREATE_PROCEDURE, name, NULL);
@ -2875,6 +2877,8 @@ void CreateAlterProcedureNode::executeCreate(thread_db* tdbb, DsqlCompilerScratc
storePrivileges(tdbb, transaction, name, obj_procedure, EXEC_PRIVILEGES);
executeAlter(tdbb, dsqlScratch, transaction, false, false);
return true;
}
bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
@ -6430,20 +6434,6 @@ void RelationNode::defineField(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch
dsql_fld* field = clause->field;
dsql_rel* relation = dsqlScratch->relation;
if (clause->createIfNotExistsOnly)
{
AutoCacheRequest request(tdbb, drq_l_rel_fld_name, DYN_REQUESTS);
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
RFL IN RDB$RELATION_FIELDS
WITH RFL.RDB$RELATION_NAME = relation->rel_name.c_str() AND
RFL.RDB$FIELD_NAME = field->fld_name.c_str()
{
return;
}
END_FOR
}
// Add the field to the relation being defined for parsing purposes.
bool permanent = false;
@ -6646,20 +6636,6 @@ void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
{
MemoryPool& pool = dsqlScratch->getPool();
if (clause->createIfNotExistsOnly)
{
AutoCacheRequest request(tdbb, drq_l_rel_con, DYN_REQUESTS);
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
RC IN RDB$RELATION_CONSTRAINTS
WITH RC.RDB$CONSTRAINT_NAME EQ clause->name.c_str() AND
RC.RDB$RELATION_NAME EQ name.c_str()
{
return;
}
END_FOR
}
switch (clause->constraintType)
{
case AddConstraintClause::CTYPE_NOT_NULL:
@ -7754,10 +7730,33 @@ void AlterRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc
switch ((*i)->type)
{
case Clause::TYPE_ADD_COLUMN:
executeBeforeTrigger();
defineField(tdbb, dsqlScratch, transaction,
static_cast<AddColumnClause*>(i->getObject()), -1, NULL);
{
const auto addColumnClause = static_cast<AddColumnClause*>(i->getObject());
bool createColumn = true;
if (addColumnClause->createIfNotExistsOnly)
{
AutoCacheRequest request(tdbb, drq_l_rel_fld_name, DYN_REQUESTS);
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
RFL IN RDB$RELATION_FIELDS
WITH RFL.RDB$RELATION_NAME = relation->rel_name.c_str() AND
RFL.RDB$FIELD_NAME = addColumnClause->field->fld_name.c_str()
{
createColumn = false;
break;
}
END_FOR
}
if (createColumn)
{
executeBeforeTrigger();
defineField(tdbb, dsqlScratch, transaction, addColumnClause, -1, nullptr);
}
break;
}
case Clause::TYPE_ALTER_COL_TYPE:
executeBeforeTrigger();
@ -7937,16 +7936,46 @@ void AlterRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc
}
case Clause::TYPE_ADD_CONSTRAINT:
executeBeforeTrigger();
makeConstraint(tdbb, dsqlScratch, transaction,
static_cast<AddConstraintClause*>(i->getObject()), constraints);
break;
case Clause::TYPE_DROP_CONSTRAINT:
{
CreateDropConstraint& dropConstraint = constraints.add();
dropConstraint.name = static_cast<const DropConstraintClause*>(i->getObject())->name;
dropConstraint.silent = static_cast<const DropConstraintClause*>(i->getObject())->silent;
const bool silent = (*i)->type == Clause::TYPE_ADD_CONSTRAINT ?
static_cast<AddConstraintClause*>(i->getObject())->createIfNotExistsOnly :
static_cast<DropConstraintClause*>(i->getObject())->silent;
bool found = false;
if (silent)
{
const auto& constraintName = (*i)->type == Clause::TYPE_ADD_CONSTRAINT ?
static_cast<AddConstraintClause*>(i->getObject())->name :
static_cast<DropConstraintClause*>(i->getObject())->name;
AutoCacheRequest request(tdbb, drq_l_rel_con, DYN_REQUESTS);
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
RC IN RDB$RELATION_CONSTRAINTS
WITH RC.RDB$CONSTRAINT_NAME EQ constraintName.c_str() AND
RC.RDB$RELATION_NAME EQ name.c_str()
{
found = true;
break;
}
END_FOR
}
if ((*i)->type == Clause::TYPE_ADD_CONSTRAINT && !(silent && found))
{
executeBeforeTrigger();
makeConstraint(tdbb, dsqlScratch, transaction,
static_cast<AddConstraintClause*>(i->getObject()), constraints);
}
else if ((*i)->type == Clause::TYPE_DROP_CONSTRAINT && !(silent && !found))
{
executeBeforeTrigger();
CreateDropConstraint& dropConstraint = constraints.add();
dropConstraint.name = static_cast<const DropConstraintClause*>(i->getObject())->name;
dropConstraint.silent = static_cast<const DropConstraintClause*>(i->getObject())->silent;
}
break;
}
@ -8038,15 +8067,11 @@ void AlterRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc
RC.RDB$RELATION_NAME EQ name.c_str()
{
found = true;
executeBeforeTrigger();
ERASE RC;
}
END_FOR
if (!constraint->silent && !found)
executeBeforeTrigger();
if (!found && !constraint->silent)
if (!found)
{
// msg 130: "CONSTRAINT %s does not exist."
status_exception::raise(Arg::PrivateDyn(130) << constraint->name);

View File

@ -459,7 +459,7 @@ private:
return external && external->udfModule.hasData();
}
void executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction);
bool executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction);
bool executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction,
bool secondPass, bool runTriggers);
bool executeAlterIndividualParameters(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction,
@ -599,7 +599,7 @@ protected:
}
private:
void executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction);
bool executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction);
bool executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction,
bool secondPass, bool runTriggers);
bool executeAlterIndividualParameters(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction,