]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2000-2001,2011-2012,2014 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
3 | * |
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 | |
8 | * using this file. | |
9 | * | |
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. | |
16 | */ | |
17 | ||
18 | ||
19 | // | |
20 | // DbIndex.cpp | |
21 | // | |
22 | ||
23 | #include "DbIndex.h" | |
24 | #include "AppleDatabase.h" | |
25 | #include <stdio.h> | |
26 | ||
27 | DbQueryKey::DbQueryKey(const DbConstIndex &index) | |
28 | : mIndex(index), | |
29 | mTableSection(index.table().getTableSection()) | |
30 | { | |
31 | } | |
32 | ||
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. | |
36 | ||
37 | const uint32 DbKeyComparator::kUseQueryKeyOffset; | |
38 | ||
39 | bool | |
40 | DbKeyComparator::operator () (uint32 offset1, uint32 offset2) const | |
41 | { | |
42 | ReadSection rs1, rs2; | |
43 | const ReadSection *key1, *key2; | |
44 | ||
45 | // get the read sections to compare | |
46 | ||
47 | if (offset1 == kUseQueryKeyOffset) | |
48 | key1 = &mKey.mKeyData; | |
49 | else { | |
50 | rs1 = mKey.mTableSection.subsection(offset1); | |
51 | key1 = &rs1; | |
52 | } | |
53 | ||
54 | if (offset2 == kUseQueryKeyOffset) | |
55 | key2 = &mKey.mKeyData; | |
56 | else { | |
57 | rs2 = mKey.mTableSection.subsection(offset2); | |
58 | key2 = &rs2; | |
59 | } | |
60 | ||
61 | // compare the values of the attributes in the keys | |
62 | ||
63 | uint32 valueOffset1 = sizeof(uint32), valueOffset2 = sizeof(uint32); | |
64 | ||
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)); | |
69 | ||
70 | if (metaAttribute.evaluate(value1.get(), value2.get(), CSSM_DB_LESS_THAN)) | |
71 | return true; | |
72 | ||
73 | else if (metaAttribute.evaluate(value2.get(), value1.get(), CSSM_DB_LESS_THAN)) | |
74 | return false; | |
75 | } | |
76 | ||
77 | // if we are here, the keys are equal | |
78 | ||
79 | return false; | |
80 | } | |
81 | ||
82 | // Comparison used when inserting an item into an index, but otherwise | |
83 | // similar to the version above. | |
84 | ||
85 | bool | |
86 | DbIndexKey::operator < (const DbIndexKey &other) const | |
87 | { | |
88 | // compare the values of the attributes in the keys | |
89 | ||
427c49bc | 90 | uint32 numAttributes = (uint32) mIndex.mAttributes.size(); |
b1ab9ed8 A |
91 | uint32 valueOffset1 = 0, valueOffset2 = 0; |
92 | ||
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), | |
96 | valueOffset1)); | |
97 | auto_ptr<DbValue> value2(metaAttribute.createValue(other.mKeySection.subsection(other.mKeyRange), | |
98 | valueOffset2)); | |
99 | ||
100 | if (metaAttribute.evaluate(value1.get(), value2.get(), CSSM_DB_LESS_THAN)) | |
101 | return true; | |
102 | ||
103 | else if (metaAttribute.evaluate(value2.get(), value1.get(), CSSM_DB_LESS_THAN)) | |
104 | return false; | |
105 | } | |
106 | ||
107 | // if we are here, the keys are equal | |
108 | ||
109 | return false; | |
110 | } | |
111 | ||
112 | DbIndex::DbIndex(const MetaRecord &metaRecord, uint32 indexId, bool isUniqueIndex) | |
113 | : mMetaRecord(metaRecord), | |
114 | mIndexId(indexId), | |
115 | mIsUniqueIndex(isUniqueIndex) | |
116 | { | |
117 | } | |
118 | ||
119 | // Append an attribute to the vector used to form index keys. | |
120 | ||
121 | void | |
122 | DbIndex::appendAttribute(uint32 attributeId) | |
123 | { | |
124 | CSSM_DB_ATTRIBUTE_INFO info; | |
125 | info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER; | |
126 | info.Label.AttributeID = attributeId; | |
127 | ||
128 | mAttributes.push_back(&(mMetaRecord.metaAttribute(info))); | |
129 | } | |
130 | ||
131 | // Construct a new read-only index. | |
132 | ||
133 | DbConstIndex::DbConstIndex(const Table &table, uint32 indexId, bool isUniqueIndex) | |
134 | : DbIndex(table.getMetaRecord(), indexId, isUniqueIndex), | |
135 | mTable(table) | |
136 | { | |
137 | } | |
138 | ||
139 | DbConstIndex::DbConstIndex(const Table &table, const ReadSection &indexSection) | |
140 | : DbIndex(table.getMetaRecord(), indexSection.at(AtomSize), indexSection.at(2 * AtomSize)), | |
141 | mTable(table) | |
142 | { | |
143 | uint32 numAttributes = indexSection.at(3 * AtomSize); | |
144 | ||
145 | for (uint32 i = 0; i < numAttributes; i++) { | |
146 | uint32 attributeId = indexSection.at((4 + i) * AtomSize); | |
147 | appendAttribute(attributeId); | |
148 | } | |
149 | ||
150 | uint32 offset = (4 + numAttributes) * AtomSize; | |
151 | uint32 numRecords = indexSection.at(offset); | |
152 | offset += AtomSize; | |
153 | mKeyOffsetVector.overlay(numRecords, | |
154 | reinterpret_cast<const Atom *>(indexSection.range(Range(offset, numRecords * AtomSize)))); | |
155 | ||
156 | offset += numRecords * AtomSize; | |
157 | mRecordNumberVector.overlay(numRecords, | |
158 | reinterpret_cast<const Atom *>(indexSection.range(Range(offset, numRecords * AtomSize)))); | |
159 | } | |
160 | ||
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. | |
167 | ||
168 | bool | |
169 | DbConstIndex::matchesQuery(const CSSM_QUERY &query, DbQueryKey *&queryKey) const | |
170 | { | |
171 | uint32 numPredicates = query.NumSelectionPredicates; | |
172 | ||
173 | if (numPredicates == 0 || numPredicates > mAttributes.size()) | |
174 | return false; | |
175 | ||
176 | // determine which index attributes are used in the query | |
177 | ||
178 | auto_array<uint32> attributeUsed(mAttributes.size()); | |
179 | for (uint32 i = 0; i < mAttributes.size(); attributeUsed[i++] = ~(uint32)0); | |
180 | ||
181 | for (uint32 i = 0, j; i < numPredicates; i++) { | |
182 | const MetaAttribute &tableAttribute = | |
183 | mMetaRecord.metaAttribute(query.SelectionPredicate[i].Attribute.Info); | |
184 | ||
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); | |
190 | else { | |
191 | // the jth index component is the ith predicate in the query | |
192 | attributeUsed[j] = i; | |
193 | break; | |
194 | } | |
195 | } | |
196 | } | |
197 | ||
198 | if (j == mAttributes.size()) { | |
199 | // the predicate attribute is not in the index, so return failure | |
200 | return false; | |
201 | } | |
202 | } | |
203 | ||
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 | |
206 | ||
427c49bc | 207 | long lastIndex; |
b1ab9ed8 A |
208 | for (lastIndex = mAttributes.size() - 1; (lastIndex >= 0) && (attributeUsed[lastIndex] == ~(uint32)0); |
209 | lastIndex--); | |
210 | ||
211 | if (lastIndex != numPredicates - 1) | |
212 | return false; | |
213 | ||
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 | |
216 | ||
217 | CSSM_DB_OPERATOR op; | |
218 | ||
219 | if (numPredicates > 1) { | |
220 | if (query.Conjunctive != CSSM_DB_AND) | |
221 | return false; | |
222 | ||
223 | for (uint32 i = 0; i < numPredicates; i++) | |
224 | if (query.SelectionPredicate[i].DbOperator != CSSM_DB_EQUAL) | |
225 | return false; | |
226 | ||
227 | op = CSSM_DB_EQUAL; | |
228 | } | |
229 | ||
230 | // for a single predicate, check the operator | |
231 | ||
232 | else { | |
233 | op = query.SelectionPredicate[0].DbOperator; | |
234 | if (op != CSSM_DB_EQUAL && op != CSSM_DB_LESS_THAN && op != CSSM_DB_GREATER_THAN) | |
235 | return false; | |
236 | } | |
237 | ||
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 | |
240 | ||
241 | queryKey = new DbQueryKey(*this); | |
242 | queryKey->mNumKeyValues = numPredicates; | |
243 | queryKey->mOp = op; | |
244 | ||
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); | |
251 | ||
252 | return true; | |
253 | } | |
254 | ||
255 | // Perform a query on an index, returning the iterators that bound the | |
256 | // returned results. | |
257 | ||
258 | void | |
259 | DbConstIndex::performQuery(const DbQueryKey &queryKey, | |
260 | DbIndexIterator &begin, DbIndexIterator &end) const | |
261 | { | |
262 | DbKeyComparator cmp(queryKey); | |
263 | ||
264 | switch (queryKey.mOp) { | |
265 | ||
266 | case CSSM_DB_EQUAL: | |
267 | { | |
268 | pair<DbIndexIterator, DbIndexIterator> result; | |
269 | result = equal_range(mKeyOffsetVector.begin(), mKeyOffsetVector.end(), | |
270 | DbKeyComparator::kUseQueryKeyOffset, cmp); | |
271 | begin = result.first; | |
272 | end = result.second; | |
273 | } | |
274 | break; | |
275 | ||
276 | case CSSM_DB_LESS_THAN: | |
277 | begin = mKeyOffsetVector.begin(); | |
278 | end = lower_bound(begin, mKeyOffsetVector.end(), | |
279 | DbKeyComparator::kUseQueryKeyOffset, cmp); | |
280 | break; | |
281 | ||
282 | case CSSM_DB_GREATER_THAN: | |
283 | end = mKeyOffsetVector.end(); | |
284 | begin = lower_bound(mKeyOffsetVector.begin(), end, | |
285 | DbKeyComparator::kUseQueryKeyOffset, cmp); | |
286 | break; | |
287 | ||
288 | default: | |
289 | CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR); | |
b1ab9ed8 A |
290 | } |
291 | } | |
292 | ||
293 | // Given an iterator as returned by performQuery(), return the read section for the record. | |
294 | ||
295 | ReadSection | |
296 | DbConstIndex::getRecordSection(DbIndexIterator iter) const | |
297 | { | |
298 | uint32 recordNumber = mRecordNumberVector[iter - mKeyOffsetVector.begin()]; | |
299 | return mTable.getRecordSection(recordNumber); | |
300 | } | |
301 | ||
302 | // Construct a mutable index from a read-only index. | |
303 | ||
304 | DbMutableIndex::DbMutableIndex(const DbConstIndex &index) | |
305 | : DbIndex(index), | |
306 | mIndexDataSize(0) | |
307 | { | |
308 | // go through the const index and copy all the entries into the | |
309 | // mutable index | |
310 | ||
311 | const ReadSection &tableSection = index.mTable.getTableSection(); | |
312 | ||
427c49bc A |
313 | size_t numRecords = index.mKeyOffsetVector.size(); |
314 | for (size_t i = 0; i < numRecords; i++) { | |
b1ab9ed8 A |
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)); | |
320 | } | |
321 | } | |
322 | ||
323 | DbMutableIndex::DbMutableIndex(const MetaRecord &metaRecord, uint32 indexId, bool isUniqueIndex) | |
324 | : DbIndex(metaRecord, indexId, isUniqueIndex), | |
325 | mIndexDataSize(0) | |
326 | { | |
327 | } | |
328 | ||
329 | DbMutableIndex::~DbMutableIndex() | |
330 | { | |
331 | } | |
332 | ||
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. | |
337 | ||
338 | void | |
339 | DbMutableIndex::removeRecord(uint32 recordNumber) | |
340 | { | |
341 | IndexMap::iterator it, temp; | |
342 | for (it = mMap.begin(); it != mMap.end(); ) { | |
343 | temp = it; it++; | |
344 | if (temp->second == recordNumber) | |
345 | mMap.erase(temp); | |
346 | } | |
347 | } | |
348 | ||
349 | // Insert a record into an index. | |
350 | ||
351 | void | |
352 | DbMutableIndex::insertRecord(uint32 recordNumber, const ReadSection &packedRecord) | |
353 | { | |
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. | |
357 | ||
427c49bc | 358 | size_t numAttributes = mAttributes.size(); |
b1ab9ed8 A |
359 | bool allSingleValued = true; |
360 | ||
427c49bc | 361 | for (size_t i = 0; i < numAttributes; i++) { |
b1ab9ed8 A |
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 | |
366 | if (mIsUniqueIndex) | |
367 | CssmError::throwMe(CSSMERR_DL_MISSING_VALUE); | |
368 | else | |
369 | return; | |
370 | } | |
371 | else if (numValues > 1) { | |
372 | allSingleValued = false; | |
373 | break; | |
374 | } | |
375 | } | |
376 | ||
377 | if (allSingleValued) | |
378 | insertRecordSingle(recordNumber, packedRecord); | |
379 | ||
380 | else { | |
381 | // recursively build all appropriate index keys, and add them to the map | |
382 | WriteSection keyData; | |
383 | insertRecordMulti(recordNumber, packedRecord, 0, keyData, 0); | |
384 | } | |
385 | } | |
386 | ||
387 | void | |
388 | DbMutableIndex::insertRecordSingle(uint32 recordNumber, const ReadSection &packedRecord) | |
389 | { | |
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); | |
395 | ||
396 | // make an index key | |
397 | DbIndexKey key(mIndexData, Range(offset, mIndexDataSize - offset), *this); | |
398 | ||
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); | |
403 | ||
404 | // insert the item into the map | |
405 | mMap.insert(IndexMap::value_type(key, recordNumber)); | |
406 | } | |
407 | ||
408 | void | |
409 | DbMutableIndex::insertRecordMulti(uint32 recordNumber, const ReadSection &packedRecord, | |
410 | uint32 attributeIndex, WriteSection &keyData, uint32 keySize) | |
411 | { | |
412 | const MetaAttribute &metaAttribute = *(mAttributes[attributeIndex]); | |
413 | uint32 numValues = metaAttribute.getNumberOfValues(packedRecord); | |
414 | ||
415 | for (uint32 i = 0; i < numValues; i++) { | |
416 | ||
417 | uint32 newKeySize = keySize; | |
418 | metaAttribute.copyValueBytes(i, packedRecord, keyData, newKeySize); | |
419 | ||
420 | if (attributeIndex + 1 == mAttributes.size()) { | |
421 | uint32 offset = mIndexDataSize; | |
422 | mIndexDataSize = mIndexData.put(mIndexDataSize, newKeySize, keyData.address()); | |
423 | mIndexData.size(mIndexDataSize); | |
424 | ||
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); | |
428 | ||
429 | mMap.insert(IndexMap::value_type(key, recordNumber)); | |
430 | } | |
431 | else | |
432 | // otherwise, recurse with the rest of the attributes | |
433 | insertRecordMulti(recordNumber, packedRecord, attributeIndex + 1, keyData, newKeySize); | |
434 | } | |
435 | } | |
436 | ||
437 | uint32 | |
438 | DbMutableIndex::writeIndex(WriteSection &ws, uint32 offset) | |
439 | { | |
440 | IndexMap::iterator it; | |
441 | ||
442 | // reserve space for the index size | |
443 | uint32 sizeOffset = offset; | |
444 | offset += AtomSize; | |
445 | ||
446 | offset = ws.put(offset, mIndexId); | |
447 | offset = ws.put(offset, mIsUniqueIndex ? 1 : 0); | |
448 | ||
427c49bc | 449 | offset = ws.put(offset, (uint32)mAttributes.size()); |
b1ab9ed8 A |
450 | for (uint32 i = 0; i < mAttributes.size(); i++) |
451 | offset = ws.put(offset, mAttributes[i]->attributeId()); | |
452 | ||
427c49bc | 453 | offset = ws.put(offset, (uint32)mMap.size()); |
b1ab9ed8 A |
454 | |
455 | // reserve space for the array of offsets to key data | |
456 | uint32 keyPtrOffset = offset; | |
457 | offset += AtomSize * mMap.size(); | |
458 | ||
459 | // write the array of record numbers | |
460 | for (it = mMap.begin(); it != mMap.end(); it++) { | |
461 | offset = ws.put(offset, it->second); | |
462 | } | |
463 | ||
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()); | |
469 | } | |
470 | ||
471 | // write the index size | |
472 | ws.put(sizeOffset, offset - sizeOffset); | |
473 | ||
474 | return offset; | |
475 | } |