persistentstorage/dbms/utable/UT_TRANS.CPP
changeset 0 08ec8eefde2f
equal deleted inserted replaced
-1:000000000000 0:08ec8eefde2f
       
     1 // Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include "UT_STD.H"
       
    17 
       
    18 // Class RDbTransaction::CNotifier
       
    19 
       
    20 NONSHARABLE_CLASS(RDbTransaction::CNotifier) : public CDbNotifier
       
    21 	{
       
    22 public:
       
    23 	inline CNotifier( RDbTransaction& aTransaction );
       
    24 	~CNotifier();
       
    25 //
       
    26 	void Event( RDbNotifier::TEvent aEvent );
       
    27 private:
       
    28 	void Complete( TInt aStatus );
       
    29 // from CDbNotifier
       
    30 	void Notify( TType aEvent, TRequestStatus& aStatus );
       
    31 	void Cancel();
       
    32 private:
       
    33 	RDbTransaction* iTransaction;
       
    34 	TRequestStatus* iStatus;
       
    35 	TInt iPending;
       
    36 	};
       
    37 
       
    38 inline RDbTransaction::CNotifier::CNotifier( RDbTransaction& aTransaction )
       
    39  :	iTransaction( &aTransaction )
       
    40 	{}
       
    41 
       
    42 RDbTransaction::CNotifier::~CNotifier()
       
    43 //
       
    44 // Cancel any outstanding request and extract from the transaction
       
    45 //
       
    46 	{
       
    47 	Cancel();
       
    48 	if ( iTransaction )
       
    49 		{
       
    50 		__ASSERT( iTransaction->iNotifier == this );
       
    51 		iTransaction->iNotifier = 0;
       
    52 		}
       
    53 	}
       
    54 
       
    55 void RDbTransaction::CNotifier::Complete( TInt aStatus )
       
    56 	{
       
    57 	if ( iStatus )
       
    58 		{
       
    59 		iPending = 0;
       
    60 		User::RequestComplete( iStatus, aStatus );
       
    61 		}
       
    62 	}
       
    63 
       
    64 void RDbTransaction::CNotifier::Notify( CDbNotifier::TType aType, TRequestStatus& aStatus )
       
    65 //
       
    66 // Request for future notification. If the database is closed complete immediately
       
    67 //
       
    68 	{
       
    69 	__ASSERT( !iStatus );
       
    70 	__ASSERT( iPending >= 0 );
       
    71 	iStatus = &aStatus;
       
    72 	if ( iPending > RDbNotifier::EUnlock )
       
    73 		Complete( iPending );
       
    74 	else if ( !iTransaction )
       
    75 		Complete( RDbNotifier::EClose );
       
    76 	else
       
    77 		{
       
    78 		iPending = aType;
       
    79 		aStatus = KRequestPending;
       
    80 		}
       
    81 	}
       
    82 
       
    83 void RDbTransaction::CNotifier::Cancel()
       
    84 	{
       
    85 	Complete( KErrCancel );
       
    86 	}
       
    87 
       
    88 void RDbTransaction::CNotifier::Event( RDbNotifier::TEvent aEvent )
       
    89 	{
       
    90 	if ( aEvent == RDbNotifier::EClose )
       
    91 		iTransaction = 0;
       
    92 	if ( iStatus )
       
    93 		{
       
    94 		__ASSERT( iPending < 0 );
       
    95 		if (aEvent == RDbNotifier::EUnlock && iPending == CDbNotifier::EChange )
       
    96 			;	// not interested in unlock events
       
    97 		else
       
    98 			Complete( aEvent );
       
    99 		}
       
   100 	else
       
   101 		{
       
   102 		__ASSERT( iPending >= 0 );
       
   103 		if ( aEvent > iPending )
       
   104 			iPending = aEvent;		// save the event
       
   105 		}
       
   106 	}
       
   107 
       
   108 
       
   109 // Class RDbTransaction
       
   110 
       
   111 #ifdef _ASSERTIONS
       
   112 
       
   113 void RDbTransaction::_Invariant() const
       
   114 //
       
   115 // Invariance test
       
   116 //
       
   117 	{
       
   118 	if ( iLockCount == 0 )
       
   119 		{	// nothing must be happening in this state
       
   120 		__ASSERT( iLockState == EDbReadLock );
       
   121 		__ASSERT( iAction == EDbReadLock );
       
   122 		__ASSERT( iUpdaters == 0 );
       
   123 		return;
       
   124 		}
       
   125 	switch ( iLockState & EState )
       
   126 		{
       
   127 	default:
       
   128 		__ASSERT( 0 );
       
   129 	case EDbReadLock:
       
   130 		{
       
   131 		__ASSERT( iAction == EDbReadLock );
       
   132 		__ASSERT( iLockCount > 0 );		// someone must have a lock
       
   133 		__ASSERT( iLockCount <= iMaxLock );
       
   134 		__ASSERT( iUpdaters == 0 );
       
   135 		__ASSERT( iPrimary.iState != 0 );
       
   136 		for (TInt ii = iLockCount - 1; --ii >= 0; )
       
   137 			__ASSERT( iSharers[ii].iState != 0 );
       
   138 		}
       
   139 		break;
       
   140 	case EDbCompactLock:		// not allowed in user-transactions
       
   141 	case EDbRecoveryLock:
       
   142 		__ASSERT( iAction == iLockState );
       
   143 		__ASSERT( iLockCount == 1 );	// exactly one lock allowed
       
   144 		__ASSERT( iUpdaters == 0 );
       
   145 		__ASSERT( iPrimary.iState == 0 );
       
   146 		break;
       
   147 	case EDbXReadLock:	// intention to write. No updates but exclusive
       
   148 		__ASSERT( iLockCount == 1 );	// exactly one lock allowed
       
   149 		switch ( iAction )
       
   150 			{
       
   151 		default:
       
   152 			__ASSERT( 0 );
       
   153 		case EDbReadLock:	// must be in a transaction: cannot commit a write/schema mod when releasing a read lock
       
   154 			__ASSERT( iUpdaters == 0 );
       
   155 			__ASSERT( iPrimary.iState & static_cast<TUint>( ETransactionLock ) );
       
   156 			break;
       
   157 		case EDbWriteLock:
       
   158 			__ASSERT( iUpdaters > 0 );
       
   159 			break;
       
   160 			}
       
   161 		break;
       
   162 	case EDbWriteLock:
       
   163 	case EDbSchemaLock:
       
   164 		__ASSERT( iLockCount == 1 );	// exactly one lock allowed
       
   165 		switch ( iAction )
       
   166 			{
       
   167 		default:
       
   168 			__ASSERT( 0 );
       
   169 		case EDbReadLock:	// must be in a transaction: cannot commit a write/schema mod when releasing a read lock
       
   170 			__ASSERT( iUpdaters == 0 );
       
   171 			__ASSERT( iPrimary.iState & static_cast<TUint>( ETransactionLock ) );
       
   172 			break;
       
   173 		case EDbWriteLock:
       
   174 			__ASSERT( iUpdaters > 0 );
       
   175 			__ASSERT( ( iLockState & EState ) == EDbWriteLock || ( iPrimary.iState & static_cast<TUint>( ETransactionLock ) ) );
       
   176 			break;
       
   177 		case EDbSchemaLock:
       
   178 			__ASSERT( ( iLockState & EState ) == EDbSchemaLock );
       
   179 			__ASSERT( iUpdaters == 0 );
       
   180 			break;
       
   181 			}
       
   182 		break;
       
   183 		}
       
   184 	}
       
   185 
       
   186 template <class T> struct _InvariantFunc
       
   187 	{
       
   188 	static void Invariant( TAny* aPtr ) { ( (const T*)aPtr )->_Invariant(); }
       
   189 	};
       
   190 
       
   191 template <class T> inline TCleanupOperation _InvariantFunction( T* )
       
   192 	{ return _InvariantFunc<T>::Invariant; }
       
   193 
       
   194 struct _Invariant
       
   195 	{
       
   196 	inline _Invariant( TCleanupOperation aOp, TAny* aPtr )
       
   197 	 :	iOp( aOp ), iPtr( aPtr )
       
   198 		{ aOp( aPtr ); }
       
   199 	inline ~_Invariant()
       
   200 		{ iOp( iPtr ); }
       
   201 private:
       
   202 	TCleanupOperation iOp;
       
   203 	TAny* iPtr;
       
   204 	};
       
   205 
       
   206 #ifndef __LEAVE_EQUALS_THROW
       
   207 struct _InvariantL
       
   208 	{
       
   209 	inline _InvariantL( TCleanupOperation aOp, TAny* aPtr )
       
   210 		{ aOp( aPtr ); CleanupStack::PushL( TCleanupItem( aOp, aPtr ) ); }
       
   211 	inline ~_InvariantL()
       
   212         { CleanupStack::PopAndDestroy(); }
       
   213 	};
       
   214 #endif //__LEAVE_EQUALS_THROW__
       
   215 
       
   216 #define __INVARIANT   struct _Invariant _invariant( _InvariantFunction( this ), this );
       
   217 
       
   218 #ifdef __LEAVE_EQUALS_THROW__
       
   219 	#define __INVARIANT_L __INVARIANT
       
   220 #else
       
   221 	#define __INVARIANT_L struct _InvariantL _invariant( _InvariantFunction( this ), this );
       
   222 #endif //__LEAVE_EQUALS_THROW__
       
   223 
       
   224 #else // _ASSERTIONS
       
   225 
       
   226 #define __INVARIANT   ( (void)0 )
       
   227 #define __INVARIANT_L ( (void)0 )
       
   228 
       
   229 #endif // _ASSERTIONS
       
   230 
       
   231 inline TDbLockType RDbTransaction::LockState() const
       
   232 	{ return TDbLockType( iLockState & EState ); }
       
   233 
       
   234 void RDbTransaction::Close()
       
   235 	{
       
   236 	__ASSERT( !IsLocked() );
       
   237 	User::Free( iSharers );
       
   238 	Event( RDbNotifier::EClose );
       
   239 	}
       
   240 
       
   241 void RDbTransaction::DoCommitL()
       
   242 //
       
   243 // Commit any changes
       
   244 //
       
   245 	{
       
   246 	__ASSERT_ALWAYS( ( iPrimary.iState & ~ETransactionLock ) == 0, Panic( EDbStreamsPendingOnCommit ) );
       
   247 	iLockState |= EFailed;
       
   248 	Database().FlushL( LockState() );
       
   249 	Database().SynchL( LockState() );
       
   250 	Unlock( RDbNotifier::ECommit );
       
   251 	}
       
   252 
       
   253 void RDbTransaction::DoRollback()
       
   254 //
       
   255 // Rollback any changes
       
   256 //
       
   257 	{
       
   258 	__ASSERT_ALWAYS( ( iPrimary.iState & ~ETransactionLock ) == 0, Panic( EDbStreamsPendingOnRollback ) );
       
   259 	Database().Revert( LockState() );
       
   260 	Database().Abandon( LockState() );
       
   261 	if ( LockState() >= EDbWriteLock )
       
   262 		++iRollback;
       
   263 	Unlock( RDbNotifier::ERollback );
       
   264 	}
       
   265 
       
   266 // explicit transactions
       
   267 
       
   268 void RDbTransaction::BeginL( const CDbObject& aObject )
       
   269 //
       
   270 // begin a user transaction. This first gains a shared read-lock
       
   271 //
       
   272 	{
       
   273 	__INVARIANT_L;
       
   274 	__ASSERT_ALWAYS( GetLock( aObject ) == 0, Panic( EDbBeginNestedTransaction ) );
       
   275 	ReadyL();
       
   276 	PrepareSLockL( aObject, TUint( ETransactionLock ) );
       
   277 	__ASSERT( iAction == EDbReadLock );
       
   278 	__ASSERT( iLockState == EDbReadLock );
       
   279 	++iLockCount;
       
   280 	}
       
   281 
       
   282 void RDbTransaction::CommitL( const CDbObject& aObject )
       
   283 //
       
   284 // Commit a user transaction and release the lock
       
   285 // All updates must be complete for a write-lock
       
   286 //
       
   287 	{
       
   288 	__INVARIANT_L;
       
   289 	__ASSERT_ALWAYS( InTransaction( aObject ), Panic( EDbNoCurrentTransaction ) );
       
   290 	ReadyL();
       
   291 	if ( iLockCount > 1 )
       
   292 		{
       
   293 		TLock* lock = GetLock( aObject );
       
   294 		__ASSERT( lock );
       
   295 		__ASSERT_ALWAYS( lock->iState == static_cast<TUint>( ETransactionLock ), Panic( EDbStreamsPendingOnCommit ) );
       
   296 		Unlock( *lock );
       
   297 		}
       
   298 	else
       
   299 		{
       
   300 		__ASSERT_ALWAYS( iAction == EDbReadLock, Panic( EDbUpdatesPendingOnCommit ) );
       
   301 		DoCommitL();
       
   302 		}
       
   303 	}
       
   304 
       
   305 void RDbTransaction::Rollback( const CDbObject& aObject )
       
   306 //
       
   307 // Rollback a user transaction and release the lock
       
   308 // All updates must be complete/aborted for a write-lock
       
   309 //
       
   310 	{
       
   311 	__INVARIANT;
       
   312 	__ASSERT_ALWAYS( InTransaction( aObject ), Panic( EDbNoCurrentTransaction ) );
       
   313 	if ( iLockCount > 1 )
       
   314 		{
       
   315 		TLock* lock = GetLock( aObject );
       
   316 		__ASSERT( lock );
       
   317 		__ASSERT_ALWAYS( lock->iState == static_cast<TUint>( ETransactionLock ), Panic( EDbStreamsPendingOnRollback ) );
       
   318 		Unlock( *lock );
       
   319 		}
       
   320 	else
       
   321 		{
       
   322 		__ASSERT_ALWAYS( iAction == EDbReadLock, Panic( EDbUpdatesPendingOnRollback ) );
       
   323 		DoRollback();
       
   324 		}
       
   325 	}
       
   326 
       
   327 void RDbTransaction::PrepareSLockL( const CDbObject& aObject, TUint aInitState )
       
   328 //
       
   329 // prepare to acquire a shared read lock
       
   330 // if any holder has an exclusive lock this fails
       
   331 //
       
   332 	{
       
   333 	__ASSERT( GetLock( aObject ) == 0 );	// cannot get a 2nd shared lock
       
   334 //
       
   335 	THolder h = aObject.Context();
       
   336 	if ( iLockCount == 0 )
       
   337 		{
       
   338 		iPrimary.iHolder = h;				// first lock, no other checks required
       
   339 		iPrimary.iState = aInitState;
       
   340 		}
       
   341 	else if ( iLockState != EDbReadLock )
       
   342 		__LEAVE( KErrLocked );
       
   343 	else
       
   344 		{						// allocate a Sharers-slot
       
   345 		TLock* share = iSharers;
       
   346 		if ( iLockCount == iMaxLock )
       
   347 			{
       
   348 			TInt newsize = iMaxLock + ELockListGranularity;
       
   349 			if ( newsize > EMaxLock )
       
   350 				{
       
   351 				__LEAVE( KErrLocked );
       
   352 				return;
       
   353 				}
       
   354 			iSharers = share = ( TLock* )User::ReAllocL( share, ( newsize - 1 ) * sizeof( TLock ) );
       
   355 			iMaxLock = TUint8( newsize );
       
   356 			}
       
   357 		share += iLockCount - 1;
       
   358 		share->iHolder = h;
       
   359 		share->iState = aInitState;
       
   360 		}
       
   361 	}
       
   362 
       
   363 void RDbTransaction::PrepareXLockL( const CDbObject& aObject )
       
   364 //
       
   365 // prepare to acquire an exclusive lock
       
   366 // if any other holder has a lock this fails
       
   367 //
       
   368 	{
       
   369 	THolder h = aObject.Context();
       
   370 	switch ( iLockCount )
       
   371 		{
       
   372 	case 0:					// no other holders, acquire the lock
       
   373 		iPrimary.iHolder = h;
       
   374 		iPrimary.iState = 0;		// this is not a transaction lock
       
   375 		break;
       
   376 	case 1:					// check we are the single Lock holder
       
   377 		if (iPrimary.iHolder != h)
       
   378 			__LEAVE( KErrLocked );
       
   379 		break;
       
   380 	default:				// cannot get XLock
       
   381 		__LEAVE( KErrLocked );
       
   382 		break;
       
   383 		}
       
   384 	}
       
   385 
       
   386 void RDbTransaction::Unlock( RDbNotifier::TEvent aEvent )
       
   387 //
       
   388 // Remove the last lock and signal an event to the Notifier
       
   389 //
       
   390 	{
       
   391 	__ASSERT( iLockCount == 1 );
       
   392 	__ASSERT( ( iPrimary.iState & ~ETransactionLock ) == 0 );
       
   393 	TDbLockType ls = LockState();
       
   394 	Event( ls == EDbReadLock || ls == EDbXReadLock ? RDbNotifier::EUnlock : aEvent );
       
   395 	iLockCount = 0;
       
   396 	iAction = iLockState = EDbReadLock;
       
   397 	iUpdaters = 0;
       
   398 	Database().CheckIdle();
       
   399 	}
       
   400 
       
   401 void RDbTransaction::Unlock( RDbTransaction::TLock& aLock )
       
   402 //
       
   403 // Remove a shared lock holder from the list
       
   404 //
       
   405 	{
       
   406 	__ASSERT( iLockCount > 1 );
       
   407 	__ASSERT( LockState() == EDbReadLock );
       
   408 	__ASSERT( ( aLock.iState & ~ETransactionLock ) == 0 );
       
   409 	aLock = iSharers[--iLockCount - 1];
       
   410 	}
       
   411 
       
   412 RDbTransaction::TLock* RDbTransaction::GetLock( const CDbObject& aObject )
       
   413 //
       
   414 // Test if aObject holds any lock, and return it
       
   415 //
       
   416 	{
       
   417 	const THolder h = aObject.Context();
       
   418 	TInt lc = iLockCount;
       
   419 	if ( --lc >= 0 )
       
   420 		{
       
   421 		if ( iPrimary.iHolder == h )
       
   422 			return &iPrimary;
       
   423 		if ( lc > 0 )
       
   424 			{
       
   425 			TLock* const base = iSharers;
       
   426 			TLock* l = base + lc;
       
   427 			do	{
       
   428 				if ( ( --l )->iHolder == h )
       
   429 					return l;
       
   430 				} while ( l > base );
       
   431 			}
       
   432 		}
       
   433 	return 0;
       
   434 	}
       
   435 
       
   436 TBool RDbTransaction::InTransaction( const CDbObject& aObject )
       
   437 //
       
   438 // Test if aObject holds a non-auto transaction
       
   439 //
       
   440 	{
       
   441 	__INVARIANT;
       
   442 	TLock* lock = GetLock( aObject );
       
   443 	return lock ? lock->iState & static_cast<TUint>( ETransactionLock ) : 0;
       
   444 	}
       
   445 
       
   446 void RDbTransaction::ReadPrepareL( const CDbObject& aObject )
       
   447 //
       
   448 // Check that aObject can gain a shared read lock and allocate required resources
       
   449 //
       
   450 	{
       
   451 	__INVARIANT_L;
       
   452 	if ( GetLock( aObject ) == 0 )
       
   453 		PrepareSLockL( aObject, 0 );		// prepare a S-Lock for the read
       
   454 	else if ( iAction == EDbCompactLock )	// Cannot already hold a compaction lock
       
   455 		__LEAVE( KErrAccessDenied );
       
   456 	}
       
   457 
       
   458 void RDbTransaction::ReadBegin( const CDbObject& aObject )
       
   459 //
       
   460 // Take a read-lock: ReadPrepareL(aObject) _must_ already have been called
       
   461 //
       
   462 	{
       
   463 	__INVARIANT;
       
   464 	TLock* lock = GetLock( aObject );
       
   465 	if ( !lock )
       
   466 		{
       
   467 		++iLockCount;
       
   468 		lock = GetLock( aObject );
       
   469 		__ASSERT( lock );
       
   470 		}
       
   471 	++lock->iState;
       
   472 	}
       
   473 
       
   474 void RDbTransaction::ReadRelease( const CDbObject& aObject )
       
   475 	{
       
   476 	__INVARIANT;
       
   477 	TLock* lock = GetLock( aObject );
       
   478 	__ASSERT( lock );
       
   479 	__ASSERT( ( lock->iState & ~ETransactionLock ) > 0 );
       
   480 	if ( --lock->iState == 0 )
       
   481 		{	// not transaction-lock
       
   482 		if ( iLockCount > 1 )
       
   483 			Unlock( *lock );
       
   484 		else if ( iAction == EDbReadLock )	// no other locks to this client
       
   485 			Unlock( RDbNotifier::EUnlock );
       
   486 		}
       
   487 	}
       
   488 
       
   489 void RDbTransaction::DMLCheckL()
       
   490 //
       
   491 // Check that we can open a new rowset
       
   492 //
       
   493 	{
       
   494 	__INVARIANT_L;
       
   495 	ReadyL();
       
   496 	if ( iAction > EDbCompactLock )	
       
   497 		__LEAVE( KErrAccessDenied );
       
   498 	}
       
   499 
       
   500 void RDbTransaction::DMLPrepareL( const CDbObject& aObject )
       
   501 //
       
   502 // Check that we can do DML, this should be called immediately prior to DMLBegin
       
   503 //
       
   504 	{
       
   505 	__INVARIANT_L;
       
   506 	PrepareXLockL( aObject );
       
   507 	if ( iAction>EDbWriteLock )
       
   508 		__LEAVE( KErrAccessDenied );
       
   509 	}
       
   510 
       
   511 void RDbTransaction::DMLBegin()
       
   512 //
       
   513 // A Rowset begins an update
       
   514 //
       
   515 	{
       
   516 	__INVARIANT;
       
   517 	__ASSERT( iAction == EDbReadLock || iAction == EDbWriteLock );
       
   518 	__ASSERT( ( iLockState & EFailed ) == 0 );
       
   519 	__ASSERT( iLockCount <= 1 );
       
   520 	if ( iAction == EDbReadLock )
       
   521 		iAction = EDbWriteLock;
       
   522 	if (iLockState == EDbReadLock )
       
   523 		iLockState = EDbXReadLock;		// escalate lock to exclusive as we are now writing
       
   524 	++iUpdaters;
       
   525 	iLockCount = 1;
       
   526 	}
       
   527 
       
   528 void RDbTransaction::DMLTouch()
       
   529 //
       
   530 // This must be called prior to putting DML updates
       
   531 //
       
   532 	{
       
   533 	__ASSERT( iAction == EDbWriteLock );
       
   534 	__ASSERT( iUpdaters > 0 );
       
   535 	TInt ls = iLockState;
       
   536 	if ( ls == EDbXReadLock )
       
   537 		ls = EDbWriteLock | EFailed;
       
   538 	else
       
   539 		ls |= EFailed;
       
   540 	iLockState = TUint8( ls );
       
   541 	}
       
   542 
       
   543 void RDbTransaction::DMLBeginLC()
       
   544 	{
       
   545 	DMLBegin();
       
   546 	CleanupStack::PushL( TCleanupItem( DMLAbandon, this ) );
       
   547 	DMLTouch();
       
   548 	}
       
   549 
       
   550 void RDbTransaction::DMLCommitL()
       
   551 //
       
   552 // A rowset has completed an update
       
   553 //
       
   554 	{
       
   555 	__INVARIANT_L;
       
   556 	__ASSERT( iAction == EDbWriteLock && ( iLockState & EFailed ) );
       
   557 	TInt updaters = iUpdaters - 1;
       
   558 	if ( updaters == 0 )
       
   559 		{
       
   560 		if ( ( iPrimary.iState & static_cast<TUint>( ETransactionLock ) ) == 0 )
       
   561 			{
       
   562 			__ASSERT( iLockState == ( EDbWriteLock | EFailed ) );
       
   563 			DoCommitL();		// automatic write-commit, release auto-lock
       
   564 			return;
       
   565 			}
       
   566 		iAction = EDbReadLock;
       
   567 		}
       
   568 	iUpdaters = updaters;
       
   569 	iLockState &= ~EFailed;
       
   570 	}
       
   571 
       
   572 void RDbTransaction::DMLRollback()
       
   573 //
       
   574 // Rollback a DML operation
       
   575 //
       
   576 	{
       
   577 	__INVARIANT;
       
   578 	__ASSERT( iAction == EDbWriteLock );
       
   579 	TInt updates = iUpdaters - 1;
       
   580 	if ( updates == 0 )
       
   581 		{
       
   582 		if ( ( iPrimary.iState & static_cast<TUint>( ETransactionLock ) ) == 0 )
       
   583 			{
       
   584 			__ASSERT( LockState() == EDbWriteLock || LockState() == EDbXReadLock );
       
   585 			DoRollback();		// automatic rollback now (may panic)
       
   586 			return;
       
   587 			}
       
   588 		iAction = EDbReadLock;
       
   589 		}
       
   590 	iUpdaters = updates;
       
   591 	}
       
   592 
       
   593 void RDbTransaction::DMLAbandon( TAny* aPtr )
       
   594 	{
       
   595 	STATIC_CAST( RDbTransaction*, aPtr )->DMLRollback();
       
   596 	}
       
   597 
       
   598 void RDbTransaction::DDLPrepareL( const CDbObject& aObject )
       
   599 //
       
   600 // Check that we can use the database for ddl and flush out any tables
       
   601 // should be called before DDLBegin
       
   602 //
       
   603 	{
       
   604 	__INVARIANT_L;
       
   605 	ReadyL();
       
   606 	PrepareXLockL( aObject );
       
   607 	if ( iAction != EDbReadLock || ( IsLocked() && iPrimary.iState != static_cast<TUint>( ETransactionLock ) ) )
       
   608 		__LEAVE( KErrAccessDenied );	// Cannot take sole ownership of the database
       
   609 	TInt ls = iLockState;
       
   610 	if ( ls >= EDbWriteLock )
       
   611 		{	// ensure all table data is flushed as they may be "released"
       
   612 		iLockState = TUint8( ls | EFailed );
       
   613 		Database().FlushL( EDbWriteLock );
       
   614 		iLockState = TUint8( ls );
       
   615 		}
       
   616 	}
       
   617 
       
   618 void RDbTransaction::DDLBegin()
       
   619 //
       
   620 // A DDL object is about to start ops
       
   621 //
       
   622 	{
       
   623 	__INVARIANT;
       
   624 	__ASSERT( iAction == EDbReadLock );
       
   625 	__ASSERT( ( iLockState & EFailed ) == 0 );
       
   626 	__ASSERT( iLockCount <= 1 );
       
   627 	iLockState = iAction = EDbSchemaLock;
       
   628 	iLockCount = 1;
       
   629 	}
       
   630 
       
   631 void RDbTransaction::DDLBeginLC()
       
   632 	{
       
   633 	DDLBegin();
       
   634 	CleanupStack::PushL( TCleanupItem( DDLAbandon, this ) );
       
   635 	}
       
   636 
       
   637 void RDbTransaction::DDLCommitL()
       
   638 //
       
   639 // A DDL incremental object has completed
       
   640 //
       
   641 	{
       
   642 	__INVARIANT_L;
       
   643 	__ASSERT( iAction == EDbSchemaLock );
       
   644 	if ( ( iPrimary.iState & static_cast<TUint>( ETransactionLock ) ) == 0 )
       
   645 		{
       
   646 		__ASSERT( iLockState == EDbSchemaLock );
       
   647 		DoCommitL();	// release auto-lock
       
   648 		}
       
   649 	else
       
   650 		iAction = EDbReadLock;
       
   651 	}
       
   652 
       
   653 void RDbTransaction::DDLRollback()
       
   654 //
       
   655 // Rollback a DDL operation
       
   656 //
       
   657 	{
       
   658 	__INVARIANT;
       
   659 	__ASSERT( iAction == EDbSchemaLock );
       
   660 	iLockState |= EFailed;
       
   661 	if ( ( iPrimary.iState & static_cast<TUint>( ETransactionLock ) ) == 0 )
       
   662 		{
       
   663 		__ASSERT( iLockState == ( EDbSchemaLock | EFailed ) );
       
   664 		DoRollback();		// release auto-lock
       
   665 		}
       
   666 	else
       
   667 		iAction = EDbReadLock;
       
   668 	}
       
   669 
       
   670 void RDbTransaction::DDLAbandon( TAny* aPtr )
       
   671 	{
       
   672 	STATIC_CAST( RDbTransaction*, aPtr )->DDLRollback();
       
   673 	}
       
   674 
       
   675 // recovery. Nothing else can be done at the same time as this
       
   676 
       
   677 void RDbTransaction::UtilityPrepareL( const CDbObject& aObject )
       
   678 //
       
   679 // Check that we are in a state to run a utility
       
   680 //
       
   681 	{
       
   682 	__INVARIANT_L;
       
   683 	ReadyL();
       
   684 	PrepareXLockL( aObject );
       
   685 	if ( IsLocked() )			// utilities not allowed in user transaction
       
   686 		__LEAVE( KErrAccessDenied );
       
   687 	}
       
   688 
       
   689 void RDbTransaction::UtilityBegin( CDbDatabase::TUtility aType )
       
   690 //
       
   691 // Database Recovery object is about to start
       
   692 //
       
   693 	{
       
   694 	__INVARIANT;
       
   695 	__ASSERT( !IsLocked() );
       
   696 	if ( aType == CDbDatabase::ERecover )
       
   697 		iLockState = iAction = EDbRecoveryLock;
       
   698 	else
       
   699 		iLockState = iAction = EDbCompactLock;
       
   700 	iLockCount = 1;
       
   701 	}
       
   702 
       
   703 void RDbTransaction::UtilityCommitL()
       
   704 //
       
   705 // Database Recovery has completed
       
   706 //
       
   707 	{
       
   708 	__INVARIANT_L;
       
   709 	Database().SynchL( LockState() );
       
   710 	Unlock( iAction == EDbRecoveryLock ? RDbNotifier::ERecover : RDbNotifier::EUnlock );	// release auto-lock
       
   711 	}
       
   712 
       
   713 void RDbTransaction::UtilityRollback()
       
   714 	{
       
   715 	__INVARIANT;
       
   716 	Database().Revert( LockState() );
       
   717 	Unlock( RDbNotifier::EUnlock );	// release auto-lock
       
   718 	}
       
   719 
       
   720 CDbNotifier* RDbTransaction::NotifierL()
       
   721 //
       
   722 // Only support a single notifier for the database (server multiplexes)
       
   723 //
       
   724 	{
       
   725 	if ( iNotifier )
       
   726 		__LEAVE( KErrNotSupported );
       
   727 	return iNotifier = new( ELeave ) CNotifier( *this );
       
   728 	}
       
   729 
       
   730 void RDbTransaction::Event( RDbNotifier::TEvent aEvent )
       
   731 //
       
   732 // Report an event to the Notifier
       
   733 // If the lock was less than a write lock, report unlock only: no commit or rollback
       
   734 //
       
   735 	{
       
   736 	if ( iNotifier )
       
   737 		iNotifier->Event( aEvent );
       
   738 	}
       
   739 
       
   740