2 * Copyright (c) 2000-2001 Apple Computer, 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 zero
34 // means to use the key provided as part of the query; otherwise, the
35 // key comes from the database.
38 DbKeyComparator::operator () (uint32 offset1
, uint32 offset2
) const
41 const ReadSection
*key1
, *key2
;
43 // get the read sections to compare
46 key1
= &mKey
.mKeyData
;
48 rs1
= mKey
.mTableSection
.subsection(offset1
);
53 key2
= &mKey
.mKeyData
;
55 rs2
= mKey
.mTableSection
.subsection(offset2
);
59 // compare the values of the attributes in the keys
61 uint32 valueOffset1
= sizeof(uint32
), valueOffset2
= sizeof(uint32
);
63 for (uint32 i
= 0; i
< mKey
.mNumKeyValues
; i
++) {
64 const MetaAttribute
&metaAttribute
= *mKey
.mIndex
.mAttributes
[i
];
65 auto_ptr
<DbValue
> value1(metaAttribute
.createValue(*key1
, valueOffset1
));
66 auto_ptr
<DbValue
> value2(metaAttribute
.createValue(*key2
, valueOffset2
));
68 if (metaAttribute
.evaluate(value1
.get(), value2
.get(), CSSM_DB_LESS_THAN
))
71 else if (metaAttribute
.evaluate(value2
.get(), value1
.get(), CSSM_DB_LESS_THAN
))
75 // if we are here, the keys are equal
80 // Comparison used when inserting an item into an index, but otherwise
81 // similar to the version above.
84 DbIndexKey::operator < (const DbIndexKey
&other
) const
86 // compare the values of the attributes in the keys
88 uint32 numAttributes
= mIndex
.mAttributes
.size();
89 uint32 valueOffset1
= 0, valueOffset2
= 0;
91 for (uint32 i
= 0; i
< numAttributes
; i
++) {
92 const MetaAttribute
&metaAttribute
= *mIndex
.mAttributes
[i
];
93 auto_ptr
<DbValue
> value1(metaAttribute
.createValue(mKeySection
.subsection(mKeyRange
),
95 auto_ptr
<DbValue
> value2(metaAttribute
.createValue(other
.mKeySection
.subsection(other
.mKeyRange
),
98 if (metaAttribute
.evaluate(value1
.get(), value2
.get(), CSSM_DB_LESS_THAN
))
101 else if (metaAttribute
.evaluate(value2
.get(), value1
.get(), CSSM_DB_LESS_THAN
))
105 // if we are here, the keys are equal
110 DbIndex::DbIndex(const MetaRecord
&metaRecord
, uint32 indexId
, bool isUniqueIndex
)
111 : mMetaRecord(metaRecord
),
113 mIsUniqueIndex(isUniqueIndex
)
117 // Append an attribute to the vector used to form index keys.
120 DbIndex::appendAttribute(uint32 attributeId
)
122 CSSM_DB_ATTRIBUTE_INFO info
;
123 info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
;
124 info
.Label
.AttributeID
= attributeId
;
126 mAttributes
.push_back(&(mMetaRecord
.metaAttribute(info
)));
129 // Construct a new read-only index.
131 DbConstIndex::DbConstIndex(const Table
&table
, uint32 indexId
, bool isUniqueIndex
)
132 : DbIndex(table
.getMetaRecord(), indexId
, isUniqueIndex
),
137 DbConstIndex::DbConstIndex(const Table
&table
, const ReadSection
&indexSection
)
138 : DbIndex(table
.getMetaRecord(), indexSection
.at(AtomSize
), indexSection
.at(2 * AtomSize
)),
141 uint32 numAttributes
= indexSection
.at(3 * AtomSize
);
143 for (uint32 i
= 0; i
< numAttributes
; i
++) {
144 uint32 attributeId
= indexSection
.at((4 + i
) * AtomSize
);
145 appendAttribute(attributeId
);
148 uint32 offset
= (4 + numAttributes
) * AtomSize
;
149 uint32 numRecords
= indexSection
.at(offset
);
151 mKeyOffsetVector
.overlay(numRecords
,
152 reinterpret_cast<const uint32
*>(indexSection
.range(Range(offset
, numRecords
* AtomSize
))));
154 offset
+= numRecords
* AtomSize
;
155 mRecordNumberVector
.overlay(numRecords
,
156 reinterpret_cast<const uint32
*>(indexSection
.range(Range(offset
, numRecords
* AtomSize
))));
159 // Check to see if this index can be used to perform a given query, based on
160 // the attributes used in the query and their order. They must be a prefix
161 // of the index key attributes. If there is more than one attribute, all of the
162 // operators must be EQUAL and the conjunctive must be AND; this is needed to
163 // ensure that the results are a contiguous segment of the index. On success,
164 // the appropriate index key is generated from the query.
167 DbConstIndex::matchesQuery(const CSSM_QUERY
&query
, DbQueryKey
*&queryKey
) const
169 uint32 numPredicates
= query
.NumSelectionPredicates
;
171 if (numPredicates
== 0 || numPredicates
> mAttributes
.size())
174 // determine which index attributes are used in the query
176 auto_array
<uint32
> attributeUsed(mAttributes
.size());
177 for (uint32 i
= 0; i
< mAttributes
.size(); attributeUsed
[i
++] = ~0UL);
179 for (uint32 i
= 0, j
; i
< numPredicates
; i
++) {
180 const MetaAttribute
&tableAttribute
=
181 mMetaRecord
.metaAttribute(query
.SelectionPredicate
[i
].Attribute
.Info
);
183 for (j
= 0; j
< mAttributes
.size(); j
++) {
184 if (tableAttribute
.attributeId() == mAttributes
[j
]->attributeId()) {
185 if (attributeUsed
[j
] != ~0UL)
186 // invalid query: attribute appears twice
187 CssmError::throwMe(CSSMERR_DL_INVALID_QUERY
);
189 // the jth index component is the ith predicate in the query
190 attributeUsed
[j
] = i
;
196 if (j
== mAttributes
.size()) {
197 // the predicate attribute is not in the index, so return failure
202 // check that the query predicates form a prefix of the index key, which means that
203 // the first N index components are the N query predicates in some order
206 for (lastIndex
= mAttributes
.size() - 1; (lastIndex
>= 0) && (attributeUsed
[lastIndex
] == ~0UL);
209 if (lastIndex
!= numPredicates
- 1)
212 // if there is more than one predicate, the conjunctive must be AND and all the
213 // operators must be EQUAL for the compound index to be useful
217 if (numPredicates
> 1) {
218 if (query
.Conjunctive
!= CSSM_DB_AND
)
221 for (uint32 i
= 0; i
< numPredicates
; i
++)
222 if (query
.SelectionPredicate
[i
].DbOperator
!= CSSM_DB_EQUAL
)
228 // for a single predicate, check the operator
231 op
= query
.SelectionPredicate
[0].DbOperator
;
232 if (op
!= CSSM_DB_EQUAL
&& op
!= CSSM_DB_LESS_THAN
&& op
!= CSSM_DB_GREATER_THAN
)
236 // ok, after all that, we can use this index, so generate an object used as a key
237 // for this query on this index
239 queryKey
= new DbQueryKey(*this);
240 queryKey
->mNumKeyValues
= numPredicates
;
243 uint32 keyLength
= sizeof(uint32
);
244 for (uint32 i
= 0; i
< numPredicates
; i
++)
245 mAttributes
[i
]->packValue(queryKey
->mKeyData
, keyLength
,
246 *(query
.SelectionPredicate
[attributeUsed
[i
]].Attribute
.Value
));
247 queryKey
->mKeyData
.put(0, keyLength
- sizeof(uint32
));
248 queryKey
->mKeyData
.size(keyLength
);
253 // Perform a query on an index, returning the iterators that bound the
257 DbConstIndex::performQuery(const DbQueryKey
&queryKey
,
258 DbIndexIterator
&begin
, DbIndexIterator
&end
) const
260 DbKeyComparator
cmp(queryKey
);
262 switch (queryKey
.mOp
) {
266 pair
<DbIndexIterator
, DbIndexIterator
> result
;
267 result
= equal_range(mKeyOffsetVector
.begin(), mKeyOffsetVector
.end(),
268 DbQueryKey::kQueryValue
, cmp
);
269 begin
= result
.first
;
274 case CSSM_DB_LESS_THAN
:
275 begin
= mKeyOffsetVector
.begin();
276 end
= lower_bound(begin
, mKeyOffsetVector
.end(), DbQueryKey::kQueryValue
, cmp
);
279 case CSSM_DB_GREATER_THAN
:
280 end
= mKeyOffsetVector
.end();
281 begin
= lower_bound(mKeyOffsetVector
.begin(), end
, DbQueryKey::kQueryValue
, cmp
);
285 CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR
);
290 // Given an iterator as returned by performQuery(), return the read section for the record.
293 DbConstIndex::getRecordSection(DbIndexIterator iter
) const
295 uint32 recordNumber
= mRecordNumberVector
[iter
- mKeyOffsetVector
.begin()];
296 return mTable
.getRecordSection(recordNumber
);
299 // Construct a mutable index from a read-only index.
301 DbMutableIndex::DbMutableIndex(const DbConstIndex
&index
)
305 // go through the const index and copy all the entries into the
308 const ReadSection
&tableSection
= index
.mTable
.getTableSection();
310 uint32 numRecords
= index
.mKeyOffsetVector
.size();
311 for (uint32 i
= 0; i
< numRecords
; i
++) {
312 uint32 recordNumber
= index
.mRecordNumberVector
.at(i
);
313 uint32 keyOffset
= index
.mKeyOffsetVector
.at(i
);
314 uint32 keySize
= tableSection
.at(keyOffset
);
315 DbIndexKey
key(tableSection
, Range(keyOffset
+ AtomSize
, keySize
), *this);
316 mMap
.insert(IndexMap::value_type(key
, recordNumber
));
320 DbMutableIndex::DbMutableIndex(const MetaRecord
&metaRecord
, uint32 indexId
, bool isUniqueIndex
)
321 : DbIndex(metaRecord
, indexId
, isUniqueIndex
)
325 DbMutableIndex::~DbMutableIndex()
329 // Remove all entries for a record from an index. This is not an ideal implementation,
330 // since it walks the entire index. In a perfect world, we'd generate all the record's
331 // keys and lookup matching entries, deleting only those with the correct record number.
332 // But this is not a perfect world.
335 DbMutableIndex::removeRecord(uint32 recordNumber
)
337 IndexMap::iterator it
, temp
;
338 for (it
= mMap
.begin(); it
!= mMap
.end(); ) {
340 if (temp
->second
== recordNumber
)
345 // Insert a record into an index.
348 DbMutableIndex::insertRecord(uint32 recordNumber
, const ReadSection
&packedRecord
)
350 // The common case is that each indexed attribute has a single value in
351 // the record; detect and handle this separately since we can avoid an
352 // expensive recursive technique.
354 uint32 numAttributes
= mAttributes
.size();
355 bool allSingleValued
= true;
357 for (uint32 i
= 0; i
< numAttributes
; i
++) {
358 uint32 numValues
= mAttributes
[i
]->getNumberOfValues(packedRecord
);
359 if (numValues
== 0) {
360 // record does not have value required by index; for a unique index,
361 // this is an error, otherwise just don't index the record
363 CssmError::throwMe(CSSMERR_DL_MISSING_VALUE
);
367 else if (numValues
> 1) {
368 allSingleValued
= false;
374 insertRecordSingle(recordNumber
, packedRecord
);
377 // recursively build all appropriate index keys, and add them to the map
378 WriteSection keyData
;
379 insertRecordMulti(recordNumber
, packedRecord
, 0, keyData
, 0);
384 DbMutableIndex::insertRecordSingle(uint32 recordNumber
, const ReadSection
&packedRecord
)
386 // append the key values to the index data
387 uint32 offset
= mIndexDataSize
;
388 for (uint32 i
= 0; i
< mAttributes
.size(); i
++)
389 mAttributes
[i
]->copyValueBytes(0, packedRecord
, mIndexData
, mIndexDataSize
);
390 mIndexData
.size(mIndexDataSize
);
393 DbIndexKey
key(mIndexData
, Range(offset
, mIndexDataSize
- offset
), *this);
395 // if this is a unique index, check for a record with the same key
396 if (mIsUniqueIndex
&& (mMap
.find(key
) != mMap
.end()))
397 // the key already exists, which is an error
398 CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
);
400 // insert the item into the map
401 mMap
.insert(IndexMap::value_type(key
, recordNumber
));
405 DbMutableIndex::insertRecordMulti(uint32 recordNumber
, const ReadSection
&packedRecord
,
406 uint32 attributeIndex
, WriteSection
&keyData
, uint32 keySize
)
408 const MetaAttribute
&metaAttribute
= *(mAttributes
[attributeIndex
]);
409 uint32 numValues
= metaAttribute
.getNumberOfValues(packedRecord
);
411 for (uint32 i
= 0; i
< numValues
; i
++) {
413 uint32 newKeySize
= keySize
;
414 metaAttribute
.copyValueBytes(i
, packedRecord
, keyData
, newKeySize
);
416 if (attributeIndex
== mAttributes
.size()) {
417 uint32 offset
= mIndexDataSize
;
418 mIndexDataSize
= mIndexData
.put(mIndexDataSize
, newKeySize
, keyData
.address());
419 mIndexData
.size(mIndexDataSize
);
421 DbIndexKey
key(mIndexData
, Range(offset
, mIndexDataSize
- offset
), *this);
422 if (mIsUniqueIndex
&& (mMap
.find(key
) != mMap
.end()))
423 CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
);
425 mMap
.insert(IndexMap::value_type(key
, recordNumber
));
428 // otherwise, recurse with the rest of the attributes
429 insertRecordMulti(recordNumber
, packedRecord
, attributeIndex
+ 1, keyData
, newKeySize
);
434 DbMutableIndex::writeIndex(WriteSection
&ws
, uint32 offset
)
436 IndexMap::iterator it
;
438 // reserve space for the index size
439 uint32 sizeOffset
= offset
;
442 offset
= ws
.put(offset
, mIndexId
);
443 offset
= ws
.put(offset
, mIsUniqueIndex
? 1 : 0);
445 offset
= ws
.put(offset
, mAttributes
.size());
446 for (uint32 i
= 0; i
< mAttributes
.size(); i
++)
447 offset
= ws
.put(offset
, mAttributes
[i
]->attributeId());
449 offset
= ws
.put(offset
, mMap
.size());
451 // reserve space for the array of offsets to key data
452 uint32 keyPtrOffset
= offset
;
453 offset
+= AtomSize
* mMap
.size();
455 // write the array of record numbers
456 for (it
= mMap
.begin(); it
!= mMap
.end(); it
++) {
457 offset
= ws
.put(offset
, it
->second
);
460 // write the key data
461 for (it
= mMap
.begin(); it
!= mMap
.end(); it
++) {
462 keyPtrOffset
= ws
.put(keyPtrOffset
, offset
);
463 offset
= ws
.put(offset
, it
->first
.keySize());
464 offset
= ws
.put(offset
, it
->first
.keySize(), it
->first
.keyData());
467 // write the index size
468 ws
.put(sizeOffset
, offset
- sizeOffset
);