Skip to content

Commit

Permalink
Merge branch 'master' into alter_constraint
Browse files Browse the repository at this point in the history
  • Loading branch information
aafemt authored Apr 15, 2024
2 parents b286889 + 525c243 commit bebb87d
Show file tree
Hide file tree
Showing 16 changed files with 438 additions and 73 deletions.
44 changes: 41 additions & 3 deletions doc/sql.extensions/README.ddl.txt
Original file line number Diff line number Diff line change
Expand Up @@ -664,10 +664,48 @@ DROP USER [IF EXISTS] <user> [USING PLUGIN <plugin>]
DROP PACKAGE [IF EXISTS] <package>
DROP PACKAGE BODY [IF EXISTS] <package>
DROP [GLOBAL] MAPPING [IF EXISTS] <mapping>
ALTER TABLE <table> DROP [IF EXISTS] <column>
ALTER TABLE <table> DROP CONSTRAINT [IF EXISTS] <constraint>
ALTER TABLE <table> DROP [IF EXISTS] <column name>
ALTER TABLE <table> DROP CONSTRAINT [IF EXISTS] <constraint name>

2) ALTER CONSTRAINT clause for ALTER TABLE statement.
2) CREATE [IF NOT EXISTS]

Using subclause IF NOT EXISTS, it's now possible to try to create objects and do not get errors when they
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.

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.

The following statements are supported:

CREATE EXCEPTION [IF NOT EXISTS] ...
CREATE INDEX [IF NOT EXISTS] ...
CREATE PROCEDURE [IF NOT EXISTS] ...
CREATE TABLE [IF NOT EXISTS] ...
CREATE TRIGGER [IF NOT EXISTS] ...
CREATE VIEW [IF NOT EXISTS] ...
CREATE FILTER [IF NOT EXISTS] ...
CREATE DOMAIN [IF NOT EXISTS] ...
CREATE FUNCTION [IF NOT EXISTS] ...
DECLARE EXTERNAL FUNCTION [IF NOT EXISTS] ...
CREATE SHADOW [IF NOT EXISTS] ...
CREATE ROLE [IF NOT EXISTS] ...
CREATE GENERATOR [IF NOT EXISTS] ...
CREATE SEQUENCE [IF NOT EXISTS] ...
CREATE COLLATION [IF NOT EXISTS] ...
CREATE USER [IF NOT EXISTS] ...
CREATE PACKAGE [IF NOT EXISTS] ...
CREATE PACKAGE BODY [IF NOT EXISTS] ...
CREATE [GLOBAL] MAPPING [IF NOT EXISTS] ...
ALTER TABLE <table> ADD [IF NOT EXISTS] <column name> ...
ALTER TABLE <table> ADD CONSTRAINT [IF NOT EXISTS] <constraint name> ...

3) ALTER CONSTRAINT clause for ALTER TABLE statement.

Syntax:

Expand Down
1 change: 1 addition & 0 deletions src/common/security.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ class UserData :
unsigned int op;
int trustedAuth;
bool silent;
bool createIfNotExistsOnly = false;
CharField user, pass, first, last, middle, com, attr;
IntField adm, act;
CharField database, dba, dbaPassword, role;
Expand Down
85 changes: 82 additions & 3 deletions src/dsql/DdlNodes.epp
Original file line number Diff line number Diff line change
Expand Up @@ -1832,6 +1832,9 @@ void CreateAlterFunctionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch

if (package.isEmpty())
{
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_udf))
return;

executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE,
DDL_TRIGGER_CREATE_FUNCTION, name, NULL);

Expand Down Expand Up @@ -2847,6 +2850,9 @@ void CreateAlterProcedureNode::executeCreate(thread_db* tdbb, DsqlCompilerScratc

if (package.isEmpty())
{
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_procedure))
return;

executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE,
DDL_TRIGGER_CREATE_PROCEDURE, name, NULL);

Expand Down Expand Up @@ -3766,9 +3772,14 @@ void CreateAlterTriggerNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlS
void CreateAlterTriggerNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
jrd_tra* transaction)
{
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_trigger))
return;

executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_TRIGGER,
name, NULL);

DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_trigger);

store(tdbb, dsqlScratch, transaction);

executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_TRIGGER,
Expand Down Expand Up @@ -4052,9 +4063,14 @@ void CreateCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
// run all statements under savepoint control
AutoSavePoint savePoint(tdbb, transaction);

if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_collation))
return;

executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE,
DDL_TRIGGER_CREATE_COLLATION, name, NULL);

DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_collation);

AutoCacheRequest request(tdbb, drq_s_colls, DYN_REQUESTS);

STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
Expand Down Expand Up @@ -4442,9 +4458,14 @@ void CreateDomainNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch
// run all statements under savepoint control
AutoSavePoint savePoint(tdbb, transaction);

if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, nameType->name, obj_field))
return;

executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE,
DDL_TRIGGER_CREATE_DOMAIN, nameType->name, NULL);

DYN_UTIL_check_unique_name(tdbb, transaction, nameType->name, obj_field);

storeGlobalField(tdbb, transaction, nameType->name, type);

if (nameType->defaultClause || check || notNull)
Expand Down Expand Up @@ -5581,6 +5602,9 @@ void CreateAlterExceptionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratc
Attachment* const attachment = transaction->getAttachment();
const MetaString& ownerName = attachment->getEffectiveUserName();

if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_exception))
return;

executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE,
DDL_TRIGGER_CREATE_EXCEPTION, name, NULL);

Expand Down Expand Up @@ -5808,9 +5832,14 @@ void CreateAlterSequenceNode::putErrorPrefix(Firebird::Arg::StatusVector& status
void CreateAlterSequenceNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
jrd_tra* transaction)
{
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_generator))
return;

executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_SEQUENCE,
name, NULL);

DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_generator);

const SINT64 val = value.value_or(1);
SLONG initialStep = 1;
if (step.has_value())
Expand All @@ -5819,6 +5848,7 @@ void CreateAlterSequenceNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch
if (initialStep == 0)
status_exception::raise(Arg::Gds(isc_dyn_cant_use_zero_increment) << Arg::Str(name));
}

store(tdbb, transaction, name, fb_sysflag_user, val, initialStep);

executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_SEQUENCE,
Expand Down Expand Up @@ -6440,11 +6470,25 @@ void RelationNode::defineField(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch
const ObjectsArray<MetaName>* pkCols)
{
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;
dsql_rel* relation = dsqlScratch->relation;
if (relation != NULL)
{
if (!(relation->rel_flags & REL_new_relation))
Expand Down Expand Up @@ -6638,12 +6682,26 @@ bool RelationNode::defineDefault(thread_db* /*tdbb*/, DsqlCompilerScratch* dsqlS
}

// Make a constraint object from a legacy node.
void RelationNode::makeConstraint(thread_db* /*tdbb*/, DsqlCompilerScratch* dsqlScratch,
void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
jrd_tra* transaction, AddConstraintClause* clause,
ObjectsArray<CreateDropConstraint>& constraints, bool* notNull)
{
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:
Expand Down Expand Up @@ -7516,6 +7574,9 @@ void CreateRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
void CreateRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
jrd_tra* transaction)
{
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_relation))
return;

saveRelation(tdbb, dsqlScratch, name, false, true);

if (externalFile)
Expand Down Expand Up @@ -8944,6 +9005,9 @@ void CreateAlterViewNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
jrd_tra* transaction)
{
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_relation))
return;

Attachment* const attachment = transaction->tra_attachment;
const MetaString& ownerName = attachment->getEffectiveUserName();

Expand Down Expand Up @@ -10116,6 +10180,9 @@ void CreateIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
// run all statements under savepoint control
AutoSavePoint savePoint(tdbb, transaction);

if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_index))
return;

executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_INDEX,
name, NULL);

Expand Down Expand Up @@ -10558,6 +10625,9 @@ void CreateShadowNode::execute(thread_db* tdbb, DsqlCompilerScratch* /*dsqlScrat
FIRST 1 X IN RDB$FILES
WITH X.RDB$SHADOW_NUMBER EQ number
{
if (createIfNotExistsOnly)
return;

// msg 165: "Shadow %ld already exists"
status_exception::raise(Arg::PrivateDyn(165) << Arg::Num(number));
}
Expand Down Expand Up @@ -10678,6 +10748,10 @@ void CreateAlterRoleNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra

// run all statements under savepoint control
AutoSavePoint savePoint(tdbb, transaction);
MetaName dummyName;

if (createIfNotExistsOnly && isItSqlRole(tdbb, transaction, name, dummyName))
return;

executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE,
createFlag ? DDL_TRIGGER_CREATE_ROLE : DDL_TRIGGER_ALTER_ROLE, name, NULL);
Expand All @@ -10700,7 +10774,6 @@ void CreateAlterRoleNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
status_exception::raise(Arg::PrivateDyn(193) << name);
}

MetaName dummyName;
if (createFlag && isItSqlRole(tdbb, transaction, name, dummyName))
{
// msg 194: "SQL role @1 already exists"
Expand Down Expand Up @@ -10857,6 +10930,8 @@ void MappingNode::runInSecurityDb(SecDbContext* secDbContext)
{
case MAP_ADD:
ddl = "CREATE MAPPING ";
if (createIfNotExistsOnly)
ddl += "IF NOT EXISTS ";
break;
case MAP_MOD:
ddl = "ALTER MAPPING ";
Expand Down Expand Up @@ -11170,6 +11245,8 @@ void MappingNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd
case MAP_ADD:
if (found)
{
if (createIfNotExistsOnly)
return;
(Arg::Gds(isc_map_already_exists) << name).raise();
}
// fall through ...
Expand Down Expand Up @@ -11417,6 +11494,8 @@ void CreateAlterUserNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
(Arg::Gds(isc_random) << "Missing user name for ALTER CURRENT USER").raise();
}

userData->createIfNotExistsOnly = createIfNotExistsOnly;

Firebird::LocalStatus s;
CheckStatusWrapper statusWrapper(&s);

Expand Down
Loading

0 comments on commit bebb87d

Please sign in to comment.