Skip to content

Commit bebb87d

Browse files
authored
Merge branch 'master' into alter_constraint
2 parents b286889 + 525c243 commit bebb87d

File tree

16 files changed

+438
-73
lines changed

16 files changed

+438
-73
lines changed

Diff for: doc/sql.extensions/README.ddl.txt

+41-3
Original file line numberDiff line numberDiff line change
@@ -664,10 +664,48 @@ DROP USER [IF EXISTS] <user> [USING PLUGIN <plugin>]
664664
DROP PACKAGE [IF EXISTS] <package>
665665
DROP PACKAGE BODY [IF EXISTS] <package>
666666
DROP [GLOBAL] MAPPING [IF EXISTS] <mapping>
667-
ALTER TABLE <table> DROP [IF EXISTS] <column>
668-
ALTER TABLE <table> DROP CONSTRAINT [IF EXISTS] <constraint>
667+
ALTER TABLE <table> DROP [IF EXISTS] <column name>
668+
ALTER TABLE <table> DROP CONSTRAINT [IF EXISTS] <constraint name>
669669

670-
2) ALTER CONSTRAINT clause for ALTER TABLE statement.
670+
2) CREATE [IF NOT EXISTS]
671+
672+
Using subclause IF NOT EXISTS, it's now possible to try to create objects and do not get errors when they
673+
already exists.
674+
675+
For ALTER TABLE ... ADD subclause, DDL triggers are not fired if there are only IF NOT EXISTS subclauses and all
676+
of them are related to existing columns or constraints.
677+
678+
For others commands where IF NOT EXISTS is part of the main command, DDL triggers are not fired when the object
679+
already exists.
680+
681+
The engine only verifies if the name (object, column or constraint) already exists, and if yes, do nothing.
682+
It never tries to match the existing object with the one being created.
683+
684+
The following statements are supported:
685+
686+
CREATE EXCEPTION [IF NOT EXISTS] ...
687+
CREATE INDEX [IF NOT EXISTS] ...
688+
CREATE PROCEDURE [IF NOT EXISTS] ...
689+
CREATE TABLE [IF NOT EXISTS] ...
690+
CREATE TRIGGER [IF NOT EXISTS] ...
691+
CREATE VIEW [IF NOT EXISTS] ...
692+
CREATE FILTER [IF NOT EXISTS] ...
693+
CREATE DOMAIN [IF NOT EXISTS] ...
694+
CREATE FUNCTION [IF NOT EXISTS] ...
695+
DECLARE EXTERNAL FUNCTION [IF NOT EXISTS] ...
696+
CREATE SHADOW [IF NOT EXISTS] ...
697+
CREATE ROLE [IF NOT EXISTS] ...
698+
CREATE GENERATOR [IF NOT EXISTS] ...
699+
CREATE SEQUENCE [IF NOT EXISTS] ...
700+
CREATE COLLATION [IF NOT EXISTS] ...
701+
CREATE USER [IF NOT EXISTS] ...
702+
CREATE PACKAGE [IF NOT EXISTS] ...
703+
CREATE PACKAGE BODY [IF NOT EXISTS] ...
704+
CREATE [GLOBAL] MAPPING [IF NOT EXISTS] ...
705+
ALTER TABLE <table> ADD [IF NOT EXISTS] <column name> ...
706+
ALTER TABLE <table> ADD CONSTRAINT [IF NOT EXISTS] <constraint name> ...
707+
708+
3) ALTER CONSTRAINT clause for ALTER TABLE statement.
671709

672710
Syntax:
673711

Diff for: src/common/security.h

+1
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ class UserData :
220220
unsigned int op;
221221
int trustedAuth;
222222
bool silent;
223+
bool createIfNotExistsOnly = false;
223224
CharField user, pass, first, last, middle, com, attr;
224225
IntField adm, act;
225226
CharField database, dba, dbaPassword, role;

Diff for: src/dsql/DdlNodes.epp

+82-3
Original file line numberDiff line numberDiff line change
@@ -1832,6 +1832,9 @@ void CreateAlterFunctionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch
18321832

18331833
if (package.isEmpty())
18341834
{
1835+
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_udf))
1836+
return;
1837+
18351838
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE,
18361839
DDL_TRIGGER_CREATE_FUNCTION, name, NULL);
18371840

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

28482851
if (package.isEmpty())
28492852
{
2853+
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_procedure))
2854+
return;
2855+
28502856
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE,
28512857
DDL_TRIGGER_CREATE_PROCEDURE, name, NULL);
28522858

@@ -3766,9 +3772,14 @@ void CreateAlterTriggerNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlS
37663772
void CreateAlterTriggerNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
37673773
jrd_tra* transaction)
37683774
{
3775+
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_trigger))
3776+
return;
3777+
37693778
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_TRIGGER,
37703779
name, NULL);
37713780

3781+
DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_trigger);
3782+
37723783
store(tdbb, dsqlScratch, transaction);
37733784

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

4066+
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_collation))
4067+
return;
4068+
40554069
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE,
40564070
DDL_TRIGGER_CREATE_COLLATION, name, NULL);
40574071

4072+
DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_collation);
4073+
40584074
AutoCacheRequest request(tdbb, drq_s_colls, DYN_REQUESTS);
40594075

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

4461+
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, nameType->name, obj_field))
4462+
return;
4463+
44454464
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE,
44464465
DDL_TRIGGER_CREATE_DOMAIN, nameType->name, NULL);
44474466

4467+
DYN_UTIL_check_unique_name(tdbb, transaction, nameType->name, obj_field);
4468+
44484469
storeGlobalField(tdbb, transaction, nameType->name, type);
44494470

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

5605+
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_exception))
5606+
return;
5607+
55845608
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE,
55855609
DDL_TRIGGER_CREATE_EXCEPTION, name, NULL);
55865610

@@ -5808,9 +5832,14 @@ void CreateAlterSequenceNode::putErrorPrefix(Firebird::Arg::StatusVector& status
58085832
void CreateAlterSequenceNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
58095833
jrd_tra* transaction)
58105834
{
5835+
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_generator))
5836+
return;
5837+
58115838
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_SEQUENCE,
58125839
name, NULL);
58135840

5841+
DYN_UTIL_check_unique_name(tdbb, transaction, name, obj_generator);
5842+
58145843
const SINT64 val = value.value_or(1);
58155844
SLONG initialStep = 1;
58165845
if (step.has_value())
@@ -5819,6 +5848,7 @@ void CreateAlterSequenceNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch
58195848
if (initialStep == 0)
58205849
status_exception::raise(Arg::Gds(isc_dyn_cant_use_zero_increment) << Arg::Str(name));
58215850
}
5851+
58225852
store(tdbb, transaction, name, fb_sysflag_user, val, initialStep);
58235853

58245854
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_SEQUENCE,
@@ -6440,11 +6470,25 @@ void RelationNode::defineField(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch
64406470
const ObjectsArray<MetaName>* pkCols)
64416471
{
64426472
dsql_fld* field = clause->field;
6473+
dsql_rel* relation = dsqlScratch->relation;
6474+
6475+
if (clause->createIfNotExistsOnly)
6476+
{
6477+
AutoCacheRequest request(tdbb, drq_l_rel_fld_name, DYN_REQUESTS);
6478+
6479+
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
6480+
RFL IN RDB$RELATION_FIELDS
6481+
WITH RFL.RDB$RELATION_NAME = relation->rel_name.c_str() AND
6482+
RFL.RDB$FIELD_NAME = field->fld_name.c_str()
6483+
{
6484+
return;
6485+
}
6486+
END_FOR
6487+
}
64436488

64446489
// Add the field to the relation being defined for parsing purposes.
64456490

64466491
bool permanent = false;
6447-
dsql_rel* relation = dsqlScratch->relation;
64486492
if (relation != NULL)
64496493
{
64506494
if (!(relation->rel_flags & REL_new_relation))
@@ -6638,12 +6682,26 @@ bool RelationNode::defineDefault(thread_db* /*tdbb*/, DsqlCompilerScratch* dsqlS
66386682
}
66396683

66406684
// Make a constraint object from a legacy node.
6641-
void RelationNode::makeConstraint(thread_db* /*tdbb*/, DsqlCompilerScratch* dsqlScratch,
6685+
void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
66426686
jrd_tra* transaction, AddConstraintClause* clause,
66436687
ObjectsArray<CreateDropConstraint>& constraints, bool* notNull)
66446688
{
66456689
MemoryPool& pool = dsqlScratch->getPool();
66466690

6691+
if (clause->createIfNotExistsOnly)
6692+
{
6693+
AutoCacheRequest request(tdbb, drq_l_rel_con, DYN_REQUESTS);
6694+
6695+
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
6696+
RC IN RDB$RELATION_CONSTRAINTS
6697+
WITH RC.RDB$CONSTRAINT_NAME EQ clause->name.c_str() AND
6698+
RC.RDB$RELATION_NAME EQ name.c_str()
6699+
{
6700+
return;
6701+
}
6702+
END_FOR
6703+
}
6704+
66476705
switch (clause->constraintType)
66486706
{
66496707
case AddConstraintClause::CTYPE_NOT_NULL:
@@ -7516,6 +7574,9 @@ void CreateRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
75167574
void CreateRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
75177575
jrd_tra* transaction)
75187576
{
7577+
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_relation))
7578+
return;
7579+
75197580
saveRelation(tdbb, dsqlScratch, name, false, true);
75207581

75217582
if (externalFile)
@@ -8944,6 +9005,9 @@ void CreateAlterViewNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
89449005
void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
89459006
jrd_tra* transaction)
89469007
{
9008+
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_relation))
9009+
return;
9010+
89479011
Attachment* const attachment = transaction->tra_attachment;
89489012
const MetaString& ownerName = attachment->getEffectiveUserName();
89499013

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

10183+
if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, name, obj_index))
10184+
return;
10185+
1011910186
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_INDEX,
1012010187
name, NULL);
1012110188

@@ -10558,6 +10625,9 @@ void CreateShadowNode::execute(thread_db* tdbb, DsqlCompilerScratch* /*dsqlScrat
1055810625
FIRST 1 X IN RDB$FILES
1055910626
WITH X.RDB$SHADOW_NUMBER EQ number
1056010627
{
10628+
if (createIfNotExistsOnly)
10629+
return;
10630+
1056110631
// msg 165: "Shadow %ld already exists"
1056210632
status_exception::raise(Arg::PrivateDyn(165) << Arg::Num(number));
1056310633
}
@@ -10678,6 +10748,10 @@ void CreateAlterRoleNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
1067810748

1067910749
// run all statements under savepoint control
1068010750
AutoSavePoint savePoint(tdbb, transaction);
10751+
MetaName dummyName;
10752+
10753+
if (createIfNotExistsOnly && isItSqlRole(tdbb, transaction, name, dummyName))
10754+
return;
1068110755

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

10703-
MetaName dummyName;
1070410777
if (createFlag && isItSqlRole(tdbb, transaction, name, dummyName))
1070510778
{
1070610779
// msg 194: "SQL role @1 already exists"
@@ -10857,6 +10930,8 @@ void MappingNode::runInSecurityDb(SecDbContext* secDbContext)
1085710930
{
1085810931
case MAP_ADD:
1085910932
ddl = "CREATE MAPPING ";
10933+
if (createIfNotExistsOnly)
10934+
ddl += "IF NOT EXISTS ";
1086010935
break;
1086110936
case MAP_MOD:
1086210937
ddl = "ALTER MAPPING ";
@@ -11170,6 +11245,8 @@ void MappingNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd
1117011245
case MAP_ADD:
1117111246
if (found)
1117211247
{
11248+
if (createIfNotExistsOnly)
11249+
return;
1117311250
(Arg::Gds(isc_map_already_exists) << name).raise();
1117411251
}
1117511252
// fall through ...
@@ -11417,6 +11494,8 @@ void CreateAlterUserNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
1141711494
(Arg::Gds(isc_random) << "Missing user name for ALTER CURRENT USER").raise();
1141811495
}
1141911496

11497+
userData->createIfNotExistsOnly = createIfNotExistsOnly;
11498+
1142011499
Firebird::LocalStatus s;
1142111500
CheckStatusWrapper statusWrapper(&s);
1142211501

0 commit comments

Comments
 (0)