8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-02-02 10:40:38 +01:00

Improvement CORE-5704 : Avoid UPDATE of RDB$DATABASE by ALTER DATABASE statement when possible

This commit is contained in:
hvlad 2018-01-09 18:05:22 +02:00
parent dc2a7e5708
commit 62735f4cf8
7 changed files with 65 additions and 30 deletions

View File

@ -48,6 +48,7 @@
#include "../jrd/exe_proto.h"
#include "../jrd/intl_proto.h"
#include "../common/isc_f_proto.h"
#include "../jrd/lck_proto.h"
#include "../jrd/met_proto.h"
#include "../jrd/scl_proto.h"
#include "../jrd/vio_proto.h"
@ -11959,34 +11960,65 @@ bool AlterDatabaseNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
void AlterDatabaseNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
jrd_tra* transaction)
{
// Take a LCK_alter_database lock to prevent altering of the database from
// parallel transactions
if (!transaction->tra_alter_db_lock)
{
Lock* lock = FB_NEW_RPT(*transaction->tra_pool, 0) Lock(tdbb, 0, LCK_alter_database);
lock->lck_data = transaction->tra_number;
if (LCK_lock(tdbb, lock, LCK_write, transaction->getLockWait()))
transaction->tra_alter_db_lock = lock;
else
{
const TraNumber conflict_trans = LCK_read_data(tdbb, lock);
delete lock;
// msg 297: Concurrent ALTER DATABASE is not supported
Arg::PrivateDyn status(297);
if (conflict_trans)
status << Arg::Gds(isc_concurrent_transaction) << Arg::Num(conflict_trans);
status_exception::raise(status);
}
}
// run all statements under savepoint control
AutoSavePoint savePoint(tdbb, transaction);
Attachment* const attachment = transaction->tra_attachment;
dsql_dbb* dbb = transaction->getDsqlAttachment();
if (clauses & CLAUSE_DROP_DIFFERENCE)
changeBackupMode(tdbb, transaction, CLAUSE_DROP_DIFFERENCE);
SLONG dbAlloc = PageSpace::maxAlloc(tdbb->getDatabase());
SLONG start = create ? createLength + 1 : 0;
AutoCacheRequest request(tdbb, drq_m_database, DYN_REQUESTS);
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
DBB IN RDB$DATABASE
for (NestConst<DbFileClause>* i = files.begin(); i != files.end(); ++i)
{
MODIFY DBB USING
if (clauses & CLAUSE_DROP_DIFFERENCE)
changeBackupMode(tdbb, transaction, CLAUSE_DROP_DIFFERENCE);
DbFileClause* file = *i;
for (NestConst<DbFileClause>* i = files.begin(); i != files.end(); ++i)
{
DbFileClause* file = *i;
start = MAX(start, file->start);
defineFile(tdbb, transaction, 0, false, false, dbAlloc,
file->name.c_str(), start, file->length);
start += file->length;
}
start = MAX(start, file->start);
defineFile(tdbb, transaction, 0, false, false, dbAlloc,
file->name.c_str(), start, file->length);
start += file->length;
}
if (differenceFile.hasData())
defineDifference(tdbb, transaction, differenceFile.c_str());
if (differenceFile.hasData())
defineDifference(tdbb, transaction, differenceFile.c_str());
if (clauses & CLAUSE_BEGIN_BACKUP)
changeBackupMode(tdbb, transaction, CLAUSE_BEGIN_BACKUP);
if (clauses & CLAUSE_END_BACKUP)
changeBackupMode(tdbb, transaction, CLAUSE_END_BACKUP);
if (setDefaultCharSet.hasData() || setDefaultCollation.hasData() || linger >= 0 ||
ssDefiner.specified)
{
AutoCacheRequest request(tdbb, drq_m_database, DYN_REQUESTS);
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
DBB IN RDB$DATABASE
{
MODIFY DBB USING
if (setDefaultCharSet.hasData())
{
@ -12000,6 +12032,7 @@ void AlterDatabaseNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc
DBB.RDB$CHARACTER_SET_NAME.NULL = FALSE;
strcpy(DBB.RDB$CHARACTER_SET_NAME, setDefaultCharSet.c_str());
dsql_dbb* dbb = transaction->getDsqlAttachment();
dbb->dbb_dfl_charset = ""; // reset in the cache
}
@ -12021,17 +12054,12 @@ void AlterDatabaseNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc
DBB.RDB$SQL_SECURITY = ssDefiner.value ? FB_TRUE : FB_FALSE;
}
if (clauses & CLAUSE_BEGIN_BACKUP)
changeBackupMode(tdbb, transaction, CLAUSE_BEGIN_BACKUP);
if (clauses & CLAUSE_END_BACKUP)
changeBackupMode(tdbb, transaction, CLAUSE_END_BACKUP);
END_MODIFY
END_MODIFY
}
END_FOR
}
END_FOR
// Allow update above to work as a lock, preventing altering of the database from parallel transactions
// Moreover, it should load crypt plugin if it (by a miracle) wasn't already loaded yet
// Load crypt plugin if it (by a miracle) wasn't already loaded yet
if (clauses & CLAUSE_CRYPT)
{
Database* const db = tdbb->getDatabase();

View File

@ -573,6 +573,7 @@ static lck_owner_t get_owner_type(enum lck_t lock_type)
case LCK_btr_dont_gc:
case LCK_rel_gc:
case LCK_record_gc:
case LCK_alter_database:
owner_type = LCK_OWNER_attachment;
break;

View File

@ -70,7 +70,8 @@ enum lck_t {
LCK_rel_rescan, // Relation forced rescan lock
LCK_crypt, // Crypt lock for single crypt thread
LCK_crypt_status, // Notifies about changed database encryption status
LCK_record_gc // Record-level GC lock
LCK_record_gc, // Record-level GC lock
LCK_alter_database // ALTER DATABASE lock
};
// Lock owner types

View File

@ -1247,6 +1247,9 @@ void TRA_release_transaction(thread_db* tdbb, jrd_tra* transaction, Jrd::TraceTr
// Release the locks associated with the transaction
if (transaction->tra_alter_db_lock)
LCK_release(tdbb, transaction->tra_alter_db_lock);
vec<Lock*>* vector = transaction->tra_relation_locks;
if (vector)
{

View File

@ -259,6 +259,7 @@ public:
BlobIndexTree* tra_blobs; // pointer to actual list of active blobs
ArrayField* tra_arrays; // Linked list of active arrays
Lock* tra_lock; // lock for transaction
Lock* tra_alter_db_lock; // lock for ALTER DATABASE statement(s)
vec<Lock*>* tra_relation_locks; // locks for relations
TransactionBitmap* tra_commit_sub_trans; // committed sub-transactions
Savepoint* tra_save_point; // list of savepoints

View File

@ -6,7 +6,7 @@ set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUM
('2015-01-07 18:01:51', 'GFIX', 3, 134)
('1996-11-07 13:39:40', 'GPRE', 4, 1)
('2017-02-05 20:37:00', 'DSQL', 7, 41)
('2016-12-27 12:30:00', 'DYN', 8, 297)
('2018-01-08 20:22:00', 'DYN', 8, 298)
('1996-11-07 13:39:40', 'INSTALL', 10, 1)
('1996-11-07 13:38:41', 'TEST', 11, 4)
('2015-07-23 14:20:00', 'GBAK', 12, 370)

View File

@ -2071,6 +2071,7 @@ COMMIT WORK;
(NULL, 'CreateAlterRoleNode::execute', 'DdlNodes.epp', NULL, 8, 294, NULL, 'Access to SYSTEM PRIVILEGES in ROLES denied to @1', NULL, NULL);
(NULL, 'grant/revoke', 'DdlNode.epp', NULL, 8, 295, NULL, 'Only @1, DB owner @2 or user with privilege USE_GRANTED_BY_CLAUSE can use GRANTED BY clause', NULL, NULL);
('dyn_cant_use_zero_inc_ident', NULL, 'DdlNodes.epp', NULL, 8, 296, NULL, 'INCREMENT BY 0 is an illegal option for identity column @1 of table @2', NULL, NULL);
('dyn_concur_alter_database', 'AlterDatabaseNode::execute', 'DdlNodes.epp', NULL, 8, 297, NULL, 'Concurrent ALTER DATABASE is not supported', NULL, NULL);
COMMIT WORK;
-- TEST
(NULL, 'main', 'test.c', NULL, 11, 0, NULL, 'This is a modified text message', NULL, NULL);