2 * Copyright (c) 2000-2001,2011-2012,2014 Apple Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
24 #include "AppleDatabase.h"
27 DbQueryKey::DbQueryKey(const DbConstIndex
&index
)
29 mTableSection(index
.table().getTableSection())
33 // Perform a less-than comparison between two keys. An offset of
34 // kUseQueryKeyOffset means to use the key provided as part of the
35 // query; otherwise, the key comes from the database.
37 const uint32
DbKeyComparator::kUseQueryKeyOffset
;
40 DbKeyComparator::operator () (uint32 offset1
, uint32 offset2
) const
43 const ReadSection
*key1
, *key2
;
45 // get the read sections to compare
47 if (offset1
== kUseQueryKeyOffset
)
48 key1
= &mKey
.mKeyData
;
50 rs1
= mKey
.mTableSection
.subsection(offset1
);
54 if (offset2
== kUseQueryKeyOffset
)
55 key2
= &mKey
.mKeyData
;
57 rs2
= mKey
.mTableSection
.subsection(offset2
);
61 // compare the values of the attributes in the keys
63 uint32 valueOffset1
= sizeof(uint32
), valueOffset2
= sizeof(uint32
);
65 for (uint32 i
= 0; i
< mKey
.mNumKeyValues
; i
++) {
66 const MetaAttribute
&metaAttribute
= *mKey
.mIndex
.mAttributes
[i
];
67 auto_ptr
<DbValue
> value1(metaAttribute
.createValue(*key1
, valueOffset1
));
68 auto_ptr
<DbValue
> value2(metaAttribute
.createValue(*key2
, valueOffset2
));
70 if (metaAttribute
.evaluate(value1
.get(), value2
.get(), CSSM_DB_LESS_THAN
))
73 else if (metaAttribute
.evaluate(value2
.get(), value1
.get(), CSSM_DB_LESS_THAN
))
77 // if we are here, the keys are equal
82 // Comparison used when inserting an item into an index, but otherwise
83 // similar to the version above.
86 DbIndexKey::operator < (const DbIndexKey
&other
) const
88 // compare the values of the attributes in the keys
90 uint32 numAttributes
= (uint32
) mIndex
.mAttributes
.size();
91 uint32 valueOffset1
= 0, valueOffset2
= 0;
93 for (uint32 i
= 0; i
< numAttributes
; i
++) {
94 const MetaAttribute
&metaAttribute
= *mIndex
.mAttributes
[i
];
95 auto_ptr
<DbValue
> value1(metaAttribute
.createValue(mKeySection
.subsection(mKeyRange
),
97 auto_ptr
<DbValue
> value2(metaAttribute
.createValue(other
.mKeySection
.subsection(other
.mKeyRange
),
100 if (metaAttribute
.evaluate(value1
.get(), value2
.get(), CSSM_DB_LESS_THAN
))
103 else if (metaAttribute
.evaluate(value2
.get(), value1
.get(), CSSM_DB_LESS_THAN
))
107 // if we are here, the keys are equal
112 DbIndex::DbIndex(const MetaRecord
&metaRecord
, uint32 indexId
, bool isUniqueIndex
)
113 : mMetaRecord(metaRecord
),
115 mIsUniqueIndex(isUniqueIndex
)
119 // Append an attribute to the vector used to form index keys.
122 DbIndex::appendAttribute(uint32 attributeId
)
124 CSSM_DB_ATTRIBUTE_INFO info
;
125 info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
;
126 info
.Label
.AttributeID
= attributeId
;
128 mAttributes
.push_back(&(mMetaRecord
.metaAttribute(info
)));
131 // Construct a new read-only index.
133 DbConstIndex::DbConstIndex(const Table
&table
, uint32 indexId
, bool isUniqueIndex
)
134 : DbIndex(table
.getMetaRecord(), indexId
, isUniqueIndex
),
139 DbConstIndex::DbConstIndex(const Table
&table
, const ReadSection
&indexSection
)
140 : DbIndex(table
.getMetaRecord(), indexSection
.at(AtomSize
), indexSection
.at(2 * AtomSize
)),
143 uint32 numAttributes
= indexSection
.at(3 * AtomSize
);
145 for (uint32 i
= 0; i
< numAttributes
; i
++) {
146 uint32 attributeId
= indexSection
.at((4 + i
) * AtomSize
);
147 appendAttribute(attributeId
);
150 uint32 offset
= (4 + numAttributes
) * AtomSize
;
151 uint32 numRecords
= indexSection
.at(offset
);
153 mKeyOffsetVector
.overlay(numRecords
,
154 reinterpret_cast<const Atom
*>(indexSection
.range(Range(offset
, numRecords
* AtomSize
))));
156 offset
+= numRecords
* AtomSize
;
157 mRecordNumberVector
.overlay(numRecords
,
158 reinterpret_cast<const Atom
*>(indexSection
.range(Range(offset
, numRecords
* AtomSize
))));
161 // Check to see if this index can be used to perform a given query, based on
162 // the attributes used in the query and their order. They must be a prefix
163 // of the index key attributes. If there is more than one attribute, all of the
164 // operators must be EQUAL and the conjunctive must be AND; this is needed to
165 // ensure that the results are a contiguous segment of the index. On success,
166 // the appropriate index key is generated from the query.
169 DbConstIndex::matchesQuery(const CSSM_QUERY
&query
, DbQueryKey
*&queryKey
) const
171 uint32 numPredicates
= query
.NumSelectionPredicates
;
173 if (numPredicates
== 0 || numPredicates
> mAttributes
.size())
176 // determine which index attributes are used in the query
178 auto_array
<uint32
> attributeUsed(mAttributes
.size());
179 for (uint32 i
= 0; i
< mAttributes
.size(); attributeUsed
[i
++] = ~(uint32
)0);
181 for (uint32 i
= 0, j
; i
< numPredicates
; i
++) {
182 const MetaAttribute
&tableAttribute
=
183 mMetaRecord
.metaAttribute(query
.SelectionPredicate
[i
].Attribute
.Info
);
185 for (j
= 0; j
< mAttributes
.size(); j
++) {
186 if (tableAttribute
.attributeId() == mAttributes
[j
]->attributeId()) {
187 if (attributeUsed
[j
] != ~(uint32
)0)
188 // invalid query: attribute appears twice
189 CssmError::throwMe(CSSMERR_DL_INVALID_QUERY
);
191 // the jth index component is the ith predicate in the query
192 attributeUsed
[j
] = i
;
198 if (j
== mAttributes
.size()) {
199 // the predicate attribute is not in the index, so return failure
204 // check that the query predicates form a prefix of the index key, which means that
205 // the first N index components are the N query predicates in some order
208 for (lastIndex
= mAttributes
.size() - 1; (lastIndex
>= 0) && (attributeUsed
[lastIndex
] == ~(uint32
)0);
211 if (lastIndex
!= numPredicates
- 1)
214 // if there is more than one predicate, the conjunctive must be AND and all the
215 // operators must be EQUAL for the compound index to be useful
219 if (numPredicates
> 1) {
220 if (query
.Conjunctive
!= CSSM_DB_AND
)
223 for (uint32 i
= 0; i
< numPredicates
; i
++)
224 if (query
.SelectionPredicate
[i
].DbOperator
!= CSSM_DB_EQUAL
)
230 // for a single predicate, check the operator
233 op
= query
.SelectionPredicate
[0].DbOperator
;
234 if (op
!= CSSM_DB_EQUAL
&& op
!= CSSM_DB_LESS_THAN
&& op
!= CSSM_DB_GREATER_THAN
)
238 // ok, after all that, we can use this index, so generate an object used as a key
239 // for this query on this index
241 queryKey
= new DbQueryKey(*this);
242 queryKey
->mNumKeyValues
= numPredicates
;
245 uint32 keyLength
= sizeof(uint32
);
246 for (uint32 i
= 0; i
< numPredicates
; i
++)
247 mAttributes
[i
]->packValue(queryKey
->mKeyData
, keyLength
,
248 *(query
.SelectionPredicate
[attributeUsed
[i
]].Attribute
.Value
));
249 queryKey
->mKeyData
.put(0, keyLength
- sizeof(uint32
));
250 queryKey
->mKeyData
.size(keyLength
);
255 // Perform a query on an index, returning the iterators that bound the
259 DbConstIndex::performQuery(const DbQueryKey
&queryKey
,
260 DbIndexIterator
&begin
, DbIndexIterator
&end
) const
262 DbKeyComparator
cmp(queryKey
);
264 switch (queryKey
.mOp
) {
268 pair
<DbIndexIterator
, DbIndexIterator
> result
;
269 result
= equal_range(mKeyOffsetVector
.begin(), mKeyOffsetVector
.end(),
270 DbKeyComparator::kUseQueryKeyOffset
, cmp
);
271 begin
= result
.first
;
276 case CSSM_DB_LESS_THAN
:
277 begin
= mKeyOffsetVector
.begin();
278 end
= lower_bound(begin
, mKeyOffsetVector
.end(),
279 DbKeyComparator::kUseQueryKeyOffset
, cmp
);
282 case CSSM_DB_GREATER_THAN
:
283 end
= mKeyOffsetVector
.end();
284 begin
= lower_bound(mKeyOffsetVector
.begin(), end
,
285 DbKeyComparator::kUseQueryKeyOffset
, cmp
);
289 CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR
);
294 // Given an iterator as returned by performQuery(), return the read section for the record.
297 DbConstIndex::getRecordSection(DbIndexIterator iter
) const
299 uint32 recordNumber
= mRecordNumberVector
[iter
- mKeyOffsetVector
.begin()];
300 return mTable
.getRecordSection(recordNumber
);
303 // Construct a mutable index from a read-only index.
305 DbMutableIndex::DbMutableIndex(const DbConstIndex
&index
)
309 // go through the const index and copy all the entries into the
312 const ReadSection
&tableSection
= index
.mTable
.getTableSection();
314 size_t numRecords
= index
.mKeyOffsetVector
.size();
315 for (size_t i
= 0; i
< numRecords
; i
++) {
316 uint32 recordNumber
= index
.mRecordNumberVector
.at(i
);
317 uint32 keyOffset
= index
.mKeyOffsetVector
.at(i
);
318 uint32 keySize
= tableSection
.at(keyOffset
);
319 DbIndexKey
key(tableSection
, Range(keyOffset
+ AtomSize
, keySize
), *this);
320 mMap
.insert(IndexMap::value_type(key
, recordNumber
));
324 DbMutableIndex::DbMutableIndex(const MetaRecord
&metaRecord
, uint32 indexId
, bool isUniqueIndex
)
325 : DbIndex(metaRecord
, indexId
, isUniqueIndex
),
330 DbMutableIndex::~DbMutableIndex()
334 // Remove all entries for a record from an index. This is not an ideal implementation,
335 // since it walks the entire index. In a perfect world, we'd generate all the record's
336 // keys and lookup matching entries, deleting only those with the correct record number.
337 // But this is not a perfect world.
340 DbMutableIndex::removeRecord(uint32 recordNumber
)
342 IndexMap::iterator it
, temp
;
343 for (it
= mMap
.begin(); it
!= mMap
.end(); ) {
345 if (temp
->second
== recordNumber
)
350 // Insert a record into an index.
353 DbMutableIndex::insertRecord(uint32 recordNumber
, const ReadSection
&packedRecord
)
355 // The common case is that each indexed attribute has a single value in
356 // the record; detect and handle this separately since we can avoid an
357 // expensive recursive technique.
359 size_t numAttributes
= mAttributes
.size();
360 bool allSingleValued
= true;
362 for (size_t i
= 0; i
< numAttributes
; i
++) {
363 uint32 numValues
= mAttributes
[i
]->getNumberOfValues(packedRecord
);
364 if (numValues
== 0) {
365 // record does not have value required by index; for a unique index,
366 // this is an error, otherwise just don't index the record
368 CssmError::throwMe(CSSMERR_DL_MISSING_VALUE
);
372 else if (numValues
> 1) {
373 allSingleValued
= false;
379 insertRecordSingle(recordNumber
, packedRecord
);
382 // recursively build all appropriate index keys, and add them to the map
383 WriteSection keyData
;
384 insertRecordMulti(recordNumber
, packedRecord
, 0, keyData
, 0);
389 DbMutableIndex::insertRecordSingle(uint32 recordNumber
, const ReadSection
&packedRecord
)
391 // append the key values to the index data
392 uint32 offset
= mIndexDataSize
;
393 for (uint32 i
= 0; i
< mAttributes
.size(); i
++)
394 mAttributes
[i
]->copyValueBytes(0, packedRecord
, mIndexData
, mIndexDataSize
);
395 mIndexData
.size(mIndexDataSize
);
398 DbIndexKey
key(mIndexData
, Range(offset
, mIndexDataSize
- offset
), *this);
400 // if this is a unique index, check for a record with the same key
401 if (mIsUniqueIndex
&& (mMap
.find(key
) != mMap
.end()))
402 // the key already exists, which is an error
403 CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
);
405 // insert the item into the map
406 mMap
.insert(IndexMap::value_type(key
, recordNumber
));
410 DbMutableIndex::insertRecordMulti(uint32 recordNumber
, const ReadSection
&packedRecord
,
411 uint32 attributeIndex
, WriteSection
&keyData
, uint32 keySize
)
413 const MetaAttribute
&metaAttribute
= *(mAttributes
[attributeIndex
]);
414 uint32 numValues
= metaAttribute
.getNumberOfValues(packedRecord
);
416 for (uint32 i
= 0; i
< numValues
; i
++) {
418 uint32 newKeySize
= keySize
;
419 metaAttribute
.copyValueBytes(i
, packedRecord
, keyData
, newKeySize
);
421 if (attributeIndex
+ 1 == mAttributes
.size()) {
422 uint32 offset
= mIndexDataSize
;
423 mIndexDataSize
= mIndexData
.put(mIndexDataSize
, newKeySize
, keyData
.address());
424 mIndexData
.size(mIndexDataSize
);
426 DbIndexKey
key(mIndexData
, Range(offset
, mIndexDataSize
- offset
), *this);
427 if (mIsUniqueIndex
&& (mMap
.find(key
) != mMap
.end()))
428 CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
);
430 mMap
.insert(IndexMap::value_type(key
, recordNumber
));
433 // otherwise, recurse with the rest of the attributes
434 insertRecordMulti(recordNumber
, packedRecord
, attributeIndex
+ 1, keyData
, newKeySize
);
439 DbMutableIndex::writeIndex(WriteSection
&ws
, uint32 offset
)
441 IndexMap::iterator it
;
443 // reserve space for the index size
444 uint32 sizeOffset
= offset
;
447 offset
= ws
.put(offset
, mIndexId
);
448 offset
= ws
.put(offset
, mIsUniqueIndex
? 1 : 0);
450 offset
= ws
.put(offset
, (uint32
)mAttributes
.size());
451 for (uint32 i
= 0; i
< mAttributes
.size(); i
++)
452 offset
= ws
.put(offset
, mAttributes
[i
]->attributeId());
454 offset
= ws
.put(offset
, (uint32
)mMap
.size());
456 // reserve space for the array of offsets to key data
457 uint32 keyPtrOffset
= offset
;
458 offset
+= AtomSize
* mMap
.size();
460 // write the array of record numbers
461 for (it
= mMap
.begin(); it
!= mMap
.end(); it
++) {
462 offset
= ws
.put(offset
, it
->second
);
465 // write the key data
466 for (it
= mMap
.begin(); it
!= mMap
.end(); it
++) {
467 keyPtrOffset
= ws
.put(keyPtrOffset
, offset
);
468 offset
= ws
.put(offset
, it
->first
.keySize());
469 offset
= ws
.put(offset
, it
->first
.keySize(), it
->first
.keyData());
472 // write the index size
473 ws
.put(sizeOffset
, offset
- sizeOffset
);