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
);
293 // Given an iterator as returned by performQuery(), return the read section for the record.
296 DbConstIndex::getRecordSection(DbIndexIterator iter
) const
298 uint32 recordNumber
= mRecordNumberVector
[iter
- mKeyOffsetVector
.begin()];
299 return mTable
.getRecordSection(recordNumber
);
302 // Construct a mutable index from a read-only index.
304 DbMutableIndex::DbMutableIndex(const DbConstIndex
&index
)
308 // go through the const index and copy all the entries into the
311 const ReadSection
&tableSection
= index
.mTable
.getTableSection();
313 size_t numRecords
= index
.mKeyOffsetVector
.size();
314 for (size_t i
= 0; i
< numRecords
; i
++) {
315 uint32 recordNumber
= index
.mRecordNumberVector
.at(i
);
316 uint32 keyOffset
= index
.mKeyOffsetVector
.at(i
);
317 uint32 keySize
= tableSection
.at(keyOffset
);
318 DbIndexKey
key(tableSection
, Range(keyOffset
+ AtomSize
, keySize
), *this);
319 mMap
.insert(IndexMap::value_type(key
, recordNumber
));
323 DbMutableIndex::DbMutableIndex(const MetaRecord
&metaRecord
, uint32 indexId
, bool isUniqueIndex
)
324 : DbIndex(metaRecord
, indexId
, isUniqueIndex
),
329 DbMutableIndex::~DbMutableIndex()
333 // Remove all entries for a record from an index. This is not an ideal implementation,
334 // since it walks the entire index. In a perfect world, we'd generate all the record's
335 // keys and lookup matching entries, deleting only those with the correct record number.
336 // But this is not a perfect world.
339 DbMutableIndex::removeRecord(uint32 recordNumber
)
341 IndexMap::iterator it
, temp
;
342 for (it
= mMap
.begin(); it
!= mMap
.end(); ) {
344 if (temp
->second
== recordNumber
)
349 // Insert a record into an index.
352 DbMutableIndex::insertRecord(uint32 recordNumber
, const ReadSection
&packedRecord
)
354 // The common case is that each indexed attribute has a single value in
355 // the record; detect and handle this separately since we can avoid an
356 // expensive recursive technique.
358 size_t numAttributes
= mAttributes
.size();
359 bool allSingleValued
= true;
361 for (size_t i
= 0; i
< numAttributes
; i
++) {
362 uint32 numValues
= mAttributes
[i
]->getNumberOfValues(packedRecord
);
363 if (numValues
== 0) {
364 // record does not have value required by index; for a unique index,
365 // this is an error, otherwise just don't index the record
367 CssmError::throwMe(CSSMERR_DL_MISSING_VALUE
);
371 else if (numValues
> 1) {
372 allSingleValued
= false;
378 insertRecordSingle(recordNumber
, packedRecord
);
381 // recursively build all appropriate index keys, and add them to the map
382 WriteSection keyData
;
383 insertRecordMulti(recordNumber
, packedRecord
, 0, keyData
, 0);
388 DbMutableIndex::insertRecordSingle(uint32 recordNumber
, const ReadSection
&packedRecord
)
390 // append the key values to the index data
391 uint32 offset
= mIndexDataSize
;
392 for (uint32 i
= 0; i
< mAttributes
.size(); i
++)
393 mAttributes
[i
]->copyValueBytes(0, packedRecord
, mIndexData
, mIndexDataSize
);
394 mIndexData
.size(mIndexDataSize
);
397 DbIndexKey
key(mIndexData
, Range(offset
, mIndexDataSize
- offset
), *this);
399 // if this is a unique index, check for a record with the same key
400 if (mIsUniqueIndex
&& (mMap
.find(key
) != mMap
.end()))
401 // the key already exists, which is an error
402 CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
);
404 // insert the item into the map
405 mMap
.insert(IndexMap::value_type(key
, recordNumber
));
409 DbMutableIndex::insertRecordMulti(uint32 recordNumber
, const ReadSection
&packedRecord
,
410 uint32 attributeIndex
, WriteSection
&keyData
, uint32 keySize
)
412 const MetaAttribute
&metaAttribute
= *(mAttributes
[attributeIndex
]);
413 uint32 numValues
= metaAttribute
.getNumberOfValues(packedRecord
);
415 for (uint32 i
= 0; i
< numValues
; i
++) {
417 uint32 newKeySize
= keySize
;
418 metaAttribute
.copyValueBytes(i
, packedRecord
, keyData
, newKeySize
);
420 if (attributeIndex
+ 1 == mAttributes
.size()) {
421 uint32 offset
= mIndexDataSize
;
422 mIndexDataSize
= mIndexData
.put(mIndexDataSize
, newKeySize
, keyData
.address());
423 mIndexData
.size(mIndexDataSize
);
425 DbIndexKey
key(mIndexData
, Range(offset
, mIndexDataSize
- offset
), *this);
426 if (mIsUniqueIndex
&& (mMap
.find(key
) != mMap
.end()))
427 CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
);
429 mMap
.insert(IndexMap::value_type(key
, recordNumber
));
432 // otherwise, recurse with the rest of the attributes
433 insertRecordMulti(recordNumber
, packedRecord
, attributeIndex
+ 1, keyData
, newKeySize
);
438 DbMutableIndex::writeIndex(WriteSection
&ws
, uint32 offset
)
440 IndexMap::iterator it
;
442 // reserve space for the index size
443 uint32 sizeOffset
= offset
;
446 offset
= ws
.put(offset
, mIndexId
);
447 offset
= ws
.put(offset
, mIsUniqueIndex
? 1 : 0);
449 offset
= ws
.put(offset
, (uint32
)mAttributes
.size());
450 for (uint32 i
= 0; i
< mAttributes
.size(); i
++)
451 offset
= ws
.put(offset
, mAttributes
[i
]->attributeId());
453 offset
= ws
.put(offset
, (uint32
)mMap
.size());
455 // reserve space for the array of offsets to key data
456 uint32 keyPtrOffset
= offset
;
457 offset
+= AtomSize
* mMap
.size();
459 // write the array of record numbers
460 for (it
= mMap
.begin(); it
!= mMap
.end(); it
++) {
461 offset
= ws
.put(offset
, it
->second
);
464 // write the key data
465 for (it
= mMap
.begin(); it
!= mMap
.end(); it
++) {
466 keyPtrOffset
= ws
.put(keyPtrOffset
, offset
);
467 offset
= ws
.put(offset
, it
->first
.keySize());
468 offset
= ws
.put(offset
, it
->first
.keySize(), it
->first
.keyData());
471 // write the index size
472 ws
.put(sizeOffset
, offset
- sizeOffset
);