Skip to content

Commit d7eb28e

Browse files
TAWBrett Shirley (from Dev Box)
authored andcommitted
Optionally Unique Indices and JetGetLock( Key )
Support 2 knew functions: 1) JetGetLock can take a lock on the currently made key in an index rather than just the key of the current row. 2) An optionally unique index, which, if no other actions are taken, acts like a non-unique index. If a grbit is supplied during JetUpdate(), optionally unique indices are enforced as unique indices. 3) Includes an EFV because of the persisted bit indicating new index type. [Substrate:ab66b31e27e10fa2f58508106cf3f4ee54c6046c]
1 parent 5138d14 commit d7eb28e

12 files changed

Lines changed: 196 additions & 19 deletions

File tree

dev/ese/published/inc/jethdr.w

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,7 @@ typedef void (JET_API *JET_SPCATCALLBACK)( _In_ const unsigned long pgno, _In_ c
678678
#define JET_efvIndexDeferredPopulate 9540 // Adds support for deferred population of indices.
679679
#define JET_efvReservedTags 9560 // Allows adding additional reserved tags to cpage.
680680
#define JET_efvRBSTooSoonDeletes 9580 // Allows to decide if we can now perform non-revertable delete even if root page of table was moved recently by shrink or created recently.
681+
#define JET_efvOptionallyUniqueIndices 9600 // Allows creation of optionally unique indices.
681682

682683
// Special format specifiers here
683684
#define JET_efvUseEngineDefault (0x40000001) // Instructs the engine to use the maximal default supported Engine Format Version. (default)
@@ -4629,6 +4630,11 @@ typedef struct
46294630

46304631
#define JET_bitReadLock 0x00000001
46314632
#define JET_bitWriteLock 0x00000002
4633+
// end_PubEsent
4634+
#if ( JET_VERSION >= 0x0A01 )
4635+
#define JET_bitKeyLock 0x00000004
4636+
#endif // JET_VERSION >= 0x0A01
4637+
// begin_PubEsent
46324638

46334639
/* Constants for JetMove */
46344640

@@ -4705,6 +4711,9 @@ typedef struct
47054711
#define JET_bitIndexDeferredPopulateCreate 0x00100000 // Only create the index, don't actually populate it.
47064712
#define JET_bitIndexDeferredPopulateProcess 0x00200000 // Populate an index that was previously created with JET_bitIndexDeferredPopulateCreate
47074713
#endif // JET_VERSION >= 0x0A00
4714+
#if ( JET_VERSION >= 0x0A01 )
4715+
#define JET_bitIndexOptionallyUnique 0x00400000 // Index uniqueness is only enforced on updates using JET_bitUpdateEnforceOptionallyUniqueIndices
4716+
#endif // JET_VERSION >= 0x0A01
47084717

47094718
// These are not persisted anywhere. These are bits used by the 'Isam layer', a simpler C#-based
47104719
// interface to access ESE databases.
@@ -4958,6 +4967,9 @@ typedef struct
49584967
#endif // JET_VERSION >= 0x0502
49594968
// end_PubEsent
49604969
#define JET_bitUpdateNoVersion 0x00000002 // do not create rollback or versioning information for update
4970+
#if ( JET_VERSION >= 0x0A01 )
4971+
#define JET_bitUpdateEnforceOptionallyUniqueIndices 0x00000004 // Enforce optionally unique indices.
4972+
#endif // JET_VERSION >= 0x0A01
49614973
// begin_PubEsent
49624974

49634975
/* Flags for JetEscrowUpdate */

dev/ese/src/ese/bt.cxx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5161,7 +5161,7 @@ ERR ErrBTIGetReachablePageCount( FUCB* const pfucb, CPG* const pcpg )
51615161
// UNDONE: we don't need to latch the page at all. just create
51625162
// the version using the bookmark in the FUCB
51635163
//
5164-
ERR ErrBTLock( FUCB *pfucb, DIRLOCK dirlock )
5164+
ERR ErrBTLock( FUCB *pfucb, DIRLOCK dirlock, BOOKMARK &bm )
51655165
{
51665166
Assert( dirlock == writeLock
51675167
|| dirlock == readLock );
@@ -5194,7 +5194,7 @@ ERR ErrBTLock( FUCB *pfucb, DIRLOCK dirlock )
51945194
Call( pver->ErrVERCheckTransactionSize( pfucb->ppib ) );
51955195
Call( pver->ErrVERModify(
51965196
pfucb,
5197-
pfucb->bmCurr,
5197+
bm,
51985198
oper,
51995199
&prce,
52005200
NULL ) );

dev/ese/src/ese/dir.cxx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,6 +1490,20 @@ ERR ErrDIRReplace( FUCB *pfucb, const DATA& data, DIRFLAG dirflag )
14901490

14911491
// lock record by calling BTLockRecord
14921492
//
1493+
ERR ErrDIRGetLock( FUCB *pfucb, DIRLOCK dirlock, BOOKMARK &bm )
1494+
{
1495+
ERR err = JET_errSuccess;
1496+
1497+
Call( ErrBTLock( pfucb, dirlock, bm ) );
1498+
CallS( err );
1499+
1500+
return err;
1501+
1502+
HandleError:
1503+
Assert( !Pcsr( pfucb )->FLatched() );
1504+
return err;
1505+
}
1506+
14931507
ERR ErrDIRGetLock( FUCB *pfucb, DIRLOCK dirlock )
14941508
{
14951509
ERR err = JET_errSuccess;
@@ -1519,10 +1533,10 @@ ERR ErrDIRGetLock( FUCB *pfucb, DIRLOCK dirlock )
15191533
pfucb->locLogical == locAfterLast ||
15201534
pfucb->locLogical == locBeforeFirst ||
15211535
pfucb->locLogical == locOnSeekBM );
1522-
return( ErrERRCheck( JET_errNoCurrentRecord ) );
1536+
Error( ErrERRCheck( JET_errNoCurrentRecord ) );
15231537
}
15241538

1525-
Call( ErrBTLock( pfucb, dirlock ) );
1539+
Call( ErrBTLock( pfucb, dirlock, pfucb->bmCurr ) );
15261540
CallS( err );
15271541

15281542
return err;

dev/ese/src/ese/fcreate.cxx

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,6 +1440,7 @@ LOCAL ERR ErrFILEIValidateCreateIndex(
14401440
const BOOL fConditional = ( pidxcreate->cConditionalColumn > 0 );
14411441
const BOOL fPrimary = ( grbit & JET_bitIndexPrimary );
14421442
const BOOL fDeferredPopulate = ( grbit & JET_bitIndexDeferredPopulateCreate );
1443+
const BOOL fOptionallyUnique = ( grbit & JET_bitIndexOptionallyUnique );
14431444
const BOOL fUnique = ( grbit & ( JET_bitIndexUnique|JET_bitIndexPrimary ) );
14441445
const BOOL fDisallowNull = ( grbit & JET_bitIndexDisallowNull );
14451446
const BOOL fIgnoreNull = ( grbit & JET_bitIndexIgnoreNull );
@@ -1451,9 +1452,9 @@ LOCAL ERR ErrFILEIValidateCreateIndex(
14511452
const BOOL fCustomTupleLimits = ( grbit & JET_bitIndexTupleLimits );
14521453
const BOOL fTuples = ( ( grbit & JET_bitIndexTuples )
14531454
|| fCustomTupleLimits );
1454-
const BOOL fCrossProduct = ( grbit & JET_bitIndexCrossProduct );
1455-
const BOOL fNestedTable = ( grbit & JET_bitIndexNestedTable );
1456-
const BOOL fDotNetGuid = ( grbit & JET_bitIndexDotNetGuid );
1455+
const BOOL fCrossProduct = ( grbit & JET_bitIndexCrossProduct );
1456+
const BOOL fNestedTable = ( grbit & JET_bitIndexNestedTable );
1457+
const BOOL fDotNetGuid = ( grbit & JET_bitIndexDotNetGuid );
14571458
const BOOL fKeyMost = ( grbit & JET_bitIndexKeyMost );
14581459
const BOOL fDisallowTruncation = ( grbit & JET_bitIndexDisallowTruncation );
14591460

@@ -1462,6 +1463,12 @@ LOCAL ERR ErrFILEIValidateCreateIndex(
14621463
USHORT cbKeyMost = 0;
14631464
USHORT cbVarSegMac = 0;
14641465

1466+
// grbits that rely on an EFV
1467+
if ( fOptionallyUnique )
1468+
{
1469+
CallR( g_rgfmp[ ifmp ].ErrDBFormatFeatureEnabled( JET_efvOptionallyUniqueIndices ) );
1470+
}
1471+
14651472
// if index has custom key size set then set in IDB
14661473
// otherwise default to historic maximum key size of 255 bytes
14671474
//
@@ -1499,6 +1506,13 @@ LOCAL ERR ErrFILEIValidateCreateIndex(
14991506
return err;
15001507
}
15011508

1509+
// Optionally Unique is only allowed on non-unique secondary indices.
1510+
if ( fOptionallyUnique && ( fPrimary || fUnique ) )
1511+
{
1512+
err = ErrERRCheck( JET_errInvalidGrbit );
1513+
return err;
1514+
}
1515+
15021516
// do not allow both linear and cross product
15031517
//
15041518
if ( fCrossProduct && fNestedTable )
@@ -1777,6 +1791,11 @@ LOCAL ERR ErrFILEIValidateCreateIndex(
17771791
pidb->SetFDeferredPopulate();
17781792
}
17791793

1794+
if ( fOptionallyUnique )
1795+
{
1796+
pidb->SetFOptionallyUnique();
1797+
}
1798+
17801799
// not both linear and cross product
17811800
//
17821801
Assert( !( fCrossProduct && fNestedTable ) );
@@ -9554,6 +9573,7 @@ VOID IDB::SetFlagsFromGrbit( const JET_GRBIT grbit )
95549573
const BOOL fDotNetGuid = ( grbit & JET_bitIndexDotNetGuid );
95559574
const BOOL fDisallowTruncation = ( grbit & JET_bitIndexDisallowTruncation );
95569575
const BOOL fDeferredPopulate = ( grbit & JET_bitIndexDeferredPopulateCreate );
9576+
const BOOL fOptionallyUnique = ( grbit & JET_bitIndexOptionallyUnique );
95579577

95589578
ResetFlags();
95599579

@@ -9597,6 +9617,11 @@ VOID IDB::SetFlagsFromGrbit( const JET_GRBIT grbit )
95979617
SetFDeferredPopulate();
95989618
}
95999619

9620+
if ( fOptionallyUnique )
9621+
{
9622+
SetFOptionallyUnique();
9623+
}
9624+
96009625
// not both linear and cross product
96019626
//
96029627
Assert( !( fCrossProduct && fNestedTable ) );

dev/ese/src/ese/rec.cxx

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ ERR VTAPI ErrIsamGetLock( JET_SESID sesid, JET_VTID vtid, JET_GRBIT grbit )
3838
{
3939
PIB *ppib = reinterpret_cast<PIB *>( sesid );
4040
FUCB *pfucb = reinterpret_cast<FUCB *>( vtid );
41+
DIRLOCK dirlock;
4142
ERR err;
4243

4344
CallR( ErrPIBCheck( ppib ) );
@@ -47,25 +48,71 @@ ERR VTAPI ErrIsamGetLock( JET_SESID sesid, JET_VTID vtid, JET_GRBIT grbit )
4748
if ( ppib->Level() <= 0 )
4849
return ErrERRCheck( JET_errNotInTransaction );
4950

50-
if ( JET_bitReadLock == grbit )
51+
switch ( grbit & ( JET_bitWriteLock | JET_bitReadLock ) )
5152
{
52-
Call( ErrDIRGetLock( pfucb, readLock ) );
53+
case JET_bitReadLock:
54+
dirlock = readLock;
55+
break;
56+
57+
case JET_bitWriteLock:
58+
// ensure that table is updatable
59+
//
60+
CallR( ErrFUCBCheckUpdatable( pfucb ) );
61+
if ( !FFMPIsTempDB( pfucb->ifmp ) )
62+
{
63+
CallR( ErrPIBCheckUpdatable( ppib ) );
64+
}
65+
dirlock = writeLock;
66+
break;
67+
68+
default:
69+
Error( ErrERRCheck( JET_errInvalidGrbit ));
70+
break;
5371
}
54-
else if ( JET_bitWriteLock == grbit )
72+
73+
if ( grbit & JET_bitKeyLock )
5574
{
56-
// ensure that table is updatable
57-
//
58-
CallR( ErrFUCBCheckUpdatable( pfucb ) );
59-
if ( !FFMPIsTempDB( pfucb->ifmp ) )
75+
FUCB *pfucbUsed;
76+
BOOKMARK bm;
77+
78+
if ( pfucbNil == pfucb->pfucbCurIndex )
79+
{
80+
// We're using the primary index.
81+
pfucbUsed = pfucb;
82+
Assert( pfucbUsed->u.pfcb->FPrimaryIndex() );
83+
Assert( pfucbUsed->u.pfcb->Pidb() == pidbNil
84+
|| pfucbUsed->u.pfcb->Pidb()->FPrimary() );
85+
}
86+
else
6087
{
61-
CallR( ErrPIBCheckUpdatable( ppib ) );
88+
// We're using a 2ndary index.
89+
pfucbUsed = pfucb->pfucbCurIndex;
90+
Assert( pfucbUsed->u.pfcb->FTypeSecondaryIndex() );
91+
Assert( pfucbUsed->u.pfcb->Pidb() != pidbNil );
92+
Assert( !pfucbUsed->u.pfcb->Pidb()->FPrimary() );
6293
}
6394

64-
Call( ErrDIRGetLock( pfucb, writeLock ) );
95+
// Caller wants us to lock based on the search key.
96+
if ( !FKSPrepared( pfucbUsed ) )
97+
{
98+
Error( ErrERRCheck( JET_errKeyNotMade ) );
99+
}
100+
101+
if ( pfucbUsed->dataSearchKey.FNull() ||
102+
NULL == pfucbUsed->dataSearchKey.Pv() )
103+
{
104+
Expected( fFalse );
105+
Error( ErrERRCheck( JET_errKeyNotMade ) );
106+
}
107+
108+
bm.Nullify();
109+
bm.key.suffix = pfucbUsed->dataSearchKey;
110+
111+
Call( ErrDIRGetLock( pfucbUsed, dirlock, bm ) );
65112
}
66113
else
67114
{
68-
err = ErrERRCheck( JET_errInvalidGrbit );
115+
Call( ErrDIRGetLock( pfucb, dirlock ) );
69116
}
70117

71118
HandleError:

dev/ese/src/ese/recupd.cxx

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,11 @@ ERR VTAPI ErrIsamUpdate(
956956
CheckTable( ppib, pfucb );
957957
CheckSecondary( pfucb );
958958

959+
if ( grbit & JET_bitUpdateEnforceOptionallyUniqueIndices )
960+
{
961+
ppib->SetFEnforceOptionallyUniqueIndices();
962+
}
963+
959964
if ( FFUCBReplacePrepared( pfucb ) )
960965
{
961966
BOOKMARK *pbm;
@@ -997,6 +1002,8 @@ ERR VTAPI ErrIsamUpdate(
9971002
RECIFreeCopyBuffer( pfucb );
9981003
}
9991004

1005+
ppib->ResetFEnforceOptionallyUniqueIndices();
1006+
10001007
AssertDIRMaybeNoLatch( ppib, pfucb );
10011008
Assert( err != JET_errNoCurrentRecord );
10021009
return err;
@@ -2055,6 +2062,63 @@ ERR ErrRECIInsertIndexEntry(
20552062

20562063
PERFOpt( PERFIncCounterTable( cRECIndexInserts, PinstFromPfucb( pfucbIdx ), TceFromFUCB( pfucbIdx ) ) );
20572064

2065+
if ( pidb->FOptionallyUnique() && pfucbIdx->ppib->FEnforceOptionallyUniqueIndices() )
2066+
{
2067+
DIB dib;
2068+
BOOKMARK bm;
2069+
2070+
bm.Nullify();
2071+
bm.key = keyToInsert;
2072+
dib.pos = posDown;
2073+
dib.pbm = &bm;
2074+
dib.dirflag = fDIRExact;
2075+
2076+
// First, globally lock the key so no one else can insert it.
2077+
// Note: this lock is automatically released if the JetUpdate this call is part of
2078+
// fails.
2079+
Call( ErrDIRGetLock( pfucbIdx, writeLock, bm ) );
2080+
2081+
// Now, make sure the key is not already in use somewhere.
2082+
err = ErrDIRDown( pfucbIdx, &dib );
2083+
2084+
// Massage some internal "warnings". fDIRExact does not always result in the error code
2085+
// you'd expect.
2086+
switch ( err )
2087+
{
2088+
case wrnNDFoundLess:
2089+
case wrnNDFoundGreater:
2090+
// Although we landed on something, it wasn't what we wanted.
2091+
DIRUp( pfucbIdx );
2092+
err = ErrERRCheck( JET_errRecordNotFound );
2093+
break;
2094+
2095+
default:
2096+
break;
2097+
}
2098+
2099+
// Ignore warnings.
2100+
switch ( min( err, JET_errSuccess ) )
2101+
{
2102+
case JET_errSuccess:
2103+
// But, report any warnings.
2104+
CallS( err );
2105+
2106+
// There's already something with this key.
2107+
Error( ErrERRCheck( JET_errKeyDuplicate ) );
2108+
2109+
case JET_errNoCurrentRecord:
2110+
case JET_errRecordNotFound:
2111+
// Nope, not already in use.
2112+
err = JET_errSuccess;
2113+
break;
2114+
2115+
default:
2116+
// We don't really expect any other errors.
2117+
Assert( fFalse );
2118+
Error( err );
2119+
}
2120+
}
2121+
20582122
err = ErrDIRInsert(
20592123
pfucbIdx,
20602124
keyToInsert,

dev/ese/src/ese/sysver.cxx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ constexpr FormatVersions g_rgfmtversEngine[] = {
9696
{ JET_efvIndexDeferredPopulate, /* 9540 */ { 1568,260,540 }, { 8,120,260 }, { 3,0,0 } }, // [2022/03/02]
9797
{ JET_efvReservedTags, /* 9560 */ { 1568,270,560 }, { 8,120,260 }, { 3,0,0 } }, // [2022/06/17]
9898
{ JET_efvRBSTooSoonDeletes, /* 9580 */ { 1568,280,580 }, { 8,130,280 }, { 3,0,0 } }, // [2022/07/14]
99+
{ JET_efvOptionallyUniqueIndices, /* 9600 */ { 1568,290,600 }, { 8,130,280 }, { 3,0,0 } }, // [2022/07/14]
99100
};
100101

101102
constexpr INT g_cfmtversEngine = std::size( g_rgfmtversEngine );

dev/ese/src/inc/bt.hxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ ERR ErrBTContainsPage( FUCB* const pfucb, const BOOKMARK& bm, const PGNO pgno, c
159159
// **************************************
160160
// update operations
161161
//
162-
ERR ErrBTLock( FUCB *pfucb, DIRLOCK dirlock );
162+
ERR ErrBTLock( FUCB *pfucb, DIRLOCK dirlock, BOOKMARK& bookmark );
163163
ERR ErrBTReplace( FUCB * const pfucb, const DATA& data, const DIRFLAG dirflags );
164164

165165
template< typename TDelta >

dev/ese/src/inc/dir.hxx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,8 @@ ERR ErrDIRTermAppend( FUCB *pfucb );
189189
ERR ErrDIRDelete( FUCB *pfucb, DIRFLAG dirflag, RCE *prcePrimary = prceNil );
190190

191191
ERR ErrDIRReplace( FUCB *pfucb, const DATA& data, DIRFLAG dirflag );
192-
ERR ErrDIRGetLock( FUCB *pfucb, DIRLOCK dirlock );
192+
ERR ErrDIRGetLock( FUCB *pfucb, DIRLOCK dirlock ); // Pre-existing default is to use bmCurr. Use an overload
193+
ERR ErrDIRGetLock( FUCB *pfucb, DIRLOCK dirlock, BOOKMARK& bmToLock ); // to allow supplying bookmark other than that.
193194

194195
template< typename TDelta >
195196
ERR ErrDIRDelta(

dev/ese/src/inc/idb.hxx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ typedef USHORT IDXFLAG;
7171
const IDXFLAG fIDXExtendedColumns = 0x0001; // IDXSEGs are comprised of JET_COLUMNIDs, not FIDs
7272
const IDXFLAG fIDXDotNetGuid = 0x0002; // GUIDs sort according to .Net rules
7373
const IDXFLAG fIDXDeferredPopulate = 0x0004; // Index is not completely populated and so is not normally usable.
74+
const IDXFLAG fIDXOptionallyUnique = 0x0008; // Index can be used to enforce uniqueness, but doesn't require it.
7475

7576
INLINE BOOL FIDXExtendedColumns( const IDXFLAG idxflag ) { return ( idxflag & fIDXExtendedColumns ); }
7677
INLINE BOOL FIDXDotNetGUID( const IDXFLAG idxflag ) { return ( idxflag & fIDXDotNetGuid ); }
@@ -552,6 +553,10 @@ class IDB
552553
INLINE VOID SetFDeferredPopulate() { m_fidxPersisted |= fIDXDeferredPopulate; }
553554
INLINE VOID ResetFDeferredPopulate() { m_fidxPersisted &= ~fIDXDeferredPopulate; }
554555

556+
INLINE BOOL FOptionallyUnique() const { return ( m_fidxPersisted & fIDXOptionallyUnique ); }
557+
INLINE VOID SetFOptionallyUnique() { m_fidxPersisted |= fIDXOptionallyUnique; }
558+
INLINE VOID ResetFOptionallyUnique() { m_fidxPersisted &= ~fIDXOptionallyUnique; }
559+
555560
// Whether the user specified a locale when creating the index. The default locale may
556561
// still be used and persisted, but FLocaleSet() will be false in that case.
557562
// Also see FLocalizedText().

0 commit comments

Comments
 (0)