#include "DbIndex.h"
#include "AppleDatabase.h"
#include <stdio.h>
DbQueryKey::DbQueryKey(const DbConstIndex &index)
: mIndex(index),
mTableSection(index.table().getTableSection())
{
}
const uint32 DbKeyComparator::kUseQueryKeyOffset;
bool
DbKeyComparator::operator () (uint32 offset1, uint32 offset2) const
{
ReadSection rs1, rs2;
const ReadSection *key1, *key2;
if (offset1 == kUseQueryKeyOffset)
key1 = &mKey.mKeyData;
else {
rs1 = mKey.mTableSection.subsection(offset1);
key1 = &rs1;
}
if (offset2 == kUseQueryKeyOffset)
key2 = &mKey.mKeyData;
else {
rs2 = mKey.mTableSection.subsection(offset2);
key2 = &rs2;
}
uint32 valueOffset1 = sizeof(uint32), valueOffset2 = sizeof(uint32);
for (uint32 i = 0; i < mKey.mNumKeyValues; i++) {
const MetaAttribute &metaAttribute = *mKey.mIndex.mAttributes[i];
auto_ptr<DbValue> value1(metaAttribute.createValue(*key1, valueOffset1));
auto_ptr<DbValue> value2(metaAttribute.createValue(*key2, valueOffset2));
if (metaAttribute.evaluate(value1.get(), value2.get(), CSSM_DB_LESS_THAN))
return true;
else if (metaAttribute.evaluate(value2.get(), value1.get(), CSSM_DB_LESS_THAN))
return false;
}
return false;
}
bool
DbIndexKey::operator < (const DbIndexKey &other) const
{
uint32 numAttributes = (uint32) mIndex.mAttributes.size();
uint32 valueOffset1 = 0, valueOffset2 = 0;
for (uint32 i = 0; i < numAttributes; i++) {
const MetaAttribute &metaAttribute = *mIndex.mAttributes[i];
auto_ptr<DbValue> value1(metaAttribute.createValue(mKeySection.subsection(mKeyRange),
valueOffset1));
auto_ptr<DbValue> value2(metaAttribute.createValue(other.mKeySection.subsection(other.mKeyRange),
valueOffset2));
if (metaAttribute.evaluate(value1.get(), value2.get(), CSSM_DB_LESS_THAN))
return true;
else if (metaAttribute.evaluate(value2.get(), value1.get(), CSSM_DB_LESS_THAN))
return false;
}
return false;
}
DbIndex::DbIndex(const MetaRecord &metaRecord, uint32 indexId, bool isUniqueIndex)
: mMetaRecord(metaRecord),
mIndexId(indexId),
mIsUniqueIndex(isUniqueIndex)
{
}
void
DbIndex::appendAttribute(uint32 attributeId)
{
CSSM_DB_ATTRIBUTE_INFO info;
info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
info.Label.AttributeID = attributeId;
mAttributes.push_back(&(mMetaRecord.metaAttribute(info)));
}
DbConstIndex::DbConstIndex(const Table &table, uint32 indexId, bool isUniqueIndex)
: DbIndex(table.getMetaRecord(), indexId, isUniqueIndex),
mTable(table)
{
}
DbConstIndex::DbConstIndex(const Table &table, const ReadSection &indexSection)
: DbIndex(table.getMetaRecord(), indexSection.at(AtomSize), indexSection.at(2 * AtomSize)),
mTable(table)
{
uint32 numAttributes = indexSection.at(3 * AtomSize);
for (uint32 i = 0; i < numAttributes; i++) {
uint32 attributeId = indexSection.at((4 + i) * AtomSize);
appendAttribute(attributeId);
}
uint32 offset = (4 + numAttributes) * AtomSize;
uint32 numRecords = indexSection.at(offset);
offset += AtomSize;
mKeyOffsetVector.overlay(numRecords,
reinterpret_cast<const Atom *>(indexSection.range(Range(offset, numRecords * AtomSize))));
offset += numRecords * AtomSize;
mRecordNumberVector.overlay(numRecords,
reinterpret_cast<const Atom *>(indexSection.range(Range(offset, numRecords * AtomSize))));
}
bool
DbConstIndex::matchesQuery(const CSSM_QUERY &query, DbQueryKey *&queryKey) const
{
uint32 numPredicates = query.NumSelectionPredicates;
if (numPredicates == 0 || numPredicates > mAttributes.size())
return false;
auto_array<uint32> attributeUsed(mAttributes.size());
for (uint32 i = 0; i < mAttributes.size(); attributeUsed[i++] = ~(uint32)0);
for (uint32 i = 0, j; i < numPredicates; i++) {
const MetaAttribute &tableAttribute =
mMetaRecord.metaAttribute(query.SelectionPredicate[i].Attribute.Info);
for (j = 0; j < mAttributes.size(); j++) {
if (tableAttribute.attributeId() == mAttributes[j]->attributeId()) {
if (attributeUsed[j] != ~(uint32)0)
CssmError::throwMe(CSSMERR_DL_INVALID_QUERY);
else {
attributeUsed[j] = i;
break;
}
}
}
if (j == mAttributes.size()) {
return false;
}
}
long lastIndex;
for (lastIndex = mAttributes.size() - 1; (lastIndex >= 0) && (attributeUsed[lastIndex] == ~(uint32)0);
lastIndex--);
if (lastIndex != numPredicates - 1)
return false;
CSSM_DB_OPERATOR op;
if (numPredicates > 1) {
if (query.Conjunctive != CSSM_DB_AND)
return false;
for (uint32 i = 0; i < numPredicates; i++)
if (query.SelectionPredicate[i].DbOperator != CSSM_DB_EQUAL)
return false;
op = CSSM_DB_EQUAL;
}
else {
op = query.SelectionPredicate[0].DbOperator;
if (op != CSSM_DB_EQUAL && op != CSSM_DB_LESS_THAN && op != CSSM_DB_GREATER_THAN)
return false;
}
queryKey = new DbQueryKey(*this);
queryKey->mNumKeyValues = numPredicates;
queryKey->mOp = op;
uint32 keyLength = sizeof(uint32);
for (uint32 i = 0; i < numPredicates; i++)
mAttributes[i]->packValue(queryKey->mKeyData, keyLength,
*(query.SelectionPredicate[attributeUsed[i]].Attribute.Value));
queryKey->mKeyData.put(0, keyLength - sizeof(uint32));
queryKey->mKeyData.size(keyLength);
return true;
}
void
DbConstIndex::performQuery(const DbQueryKey &queryKey,
DbIndexIterator &begin, DbIndexIterator &end) const
{
DbKeyComparator cmp(queryKey);
switch (queryKey.mOp) {
case CSSM_DB_EQUAL:
{
pair<DbIndexIterator, DbIndexIterator> result;
result = equal_range(mKeyOffsetVector.begin(), mKeyOffsetVector.end(),
DbKeyComparator::kUseQueryKeyOffset, cmp);
begin = result.first;
end = result.second;
}
break;
case CSSM_DB_LESS_THAN:
begin = mKeyOffsetVector.begin();
end = lower_bound(begin, mKeyOffsetVector.end(),
DbKeyComparator::kUseQueryKeyOffset, cmp);
break;
case CSSM_DB_GREATER_THAN:
end = mKeyOffsetVector.end();
begin = lower_bound(mKeyOffsetVector.begin(), end,
DbKeyComparator::kUseQueryKeyOffset, cmp);
break;
default:
CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR);
}
}
ReadSection
DbConstIndex::getRecordSection(DbIndexIterator iter) const
{
uint32 recordNumber = mRecordNumberVector[iter - mKeyOffsetVector.begin()];
return mTable.getRecordSection(recordNumber);
}
DbMutableIndex::DbMutableIndex(const DbConstIndex &index)
: DbIndex(index),
mIndexDataSize(0)
{
const ReadSection &tableSection = index.mTable.getTableSection();
size_t numRecords = index.mKeyOffsetVector.size();
for (size_t i = 0; i < numRecords; i++) {
uint32 recordNumber = index.mRecordNumberVector.at(i);
uint32 keyOffset = index.mKeyOffsetVector.at(i);
uint32 keySize = tableSection.at(keyOffset);
DbIndexKey key(tableSection, Range(keyOffset + AtomSize, keySize), *this);
mMap.insert(IndexMap::value_type(key, recordNumber));
}
}
DbMutableIndex::DbMutableIndex(const MetaRecord &metaRecord, uint32 indexId, bool isUniqueIndex)
: DbIndex(metaRecord, indexId, isUniqueIndex),
mIndexDataSize(0)
{
}
DbMutableIndex::~DbMutableIndex()
{
}
void
DbMutableIndex::removeRecord(uint32 recordNumber)
{
IndexMap::iterator it, temp;
for (it = mMap.begin(); it != mMap.end(); ) {
temp = it; it++;
if (temp->second == recordNumber)
mMap.erase(temp);
}
}
void
DbMutableIndex::insertRecord(uint32 recordNumber, const ReadSection &packedRecord)
{
size_t numAttributes = mAttributes.size();
bool allSingleValued = true;
for (size_t i = 0; i < numAttributes; i++) {
uint32 numValues = mAttributes[i]->getNumberOfValues(packedRecord);
if (numValues == 0) {
if (mIsUniqueIndex)
CssmError::throwMe(CSSMERR_DL_MISSING_VALUE);
else
return;
}
else if (numValues > 1) {
allSingleValued = false;
break;
}
}
if (allSingleValued)
insertRecordSingle(recordNumber, packedRecord);
else {
WriteSection keyData;
insertRecordMulti(recordNumber, packedRecord, 0, keyData, 0);
}
}
void
DbMutableIndex::insertRecordSingle(uint32 recordNumber, const ReadSection &packedRecord)
{
uint32 offset = mIndexDataSize;
for (uint32 i = 0; i < mAttributes.size(); i++)
mAttributes[i]->copyValueBytes(0, packedRecord, mIndexData, mIndexDataSize);
mIndexData.size(mIndexDataSize);
DbIndexKey key(mIndexData, Range(offset, mIndexDataSize - offset), *this);
if (mIsUniqueIndex && (mMap.find(key) != mMap.end()))
CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA);
mMap.insert(IndexMap::value_type(key, recordNumber));
}
void
DbMutableIndex::insertRecordMulti(uint32 recordNumber, const ReadSection &packedRecord,
uint32 attributeIndex, WriteSection &keyData, uint32 keySize)
{
const MetaAttribute &metaAttribute = *(mAttributes[attributeIndex]);
uint32 numValues = metaAttribute.getNumberOfValues(packedRecord);
for (uint32 i = 0; i < numValues; i++) {
uint32 newKeySize = keySize;
metaAttribute.copyValueBytes(i, packedRecord, keyData, newKeySize);
if (attributeIndex + 1 == mAttributes.size()) {
uint32 offset = mIndexDataSize;
mIndexDataSize = mIndexData.put(mIndexDataSize, newKeySize, keyData.address());
mIndexData.size(mIndexDataSize);
DbIndexKey key(mIndexData, Range(offset, mIndexDataSize - offset), *this);
if (mIsUniqueIndex && (mMap.find(key) != mMap.end()))
CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA);
mMap.insert(IndexMap::value_type(key, recordNumber));
}
else
insertRecordMulti(recordNumber, packedRecord, attributeIndex + 1, keyData, newKeySize);
}
}
uint32
DbMutableIndex::writeIndex(WriteSection &ws, uint32 offset)
{
IndexMap::iterator it;
uint32 sizeOffset = offset;
offset += AtomSize;
offset = ws.put(offset, mIndexId);
offset = ws.put(offset, mIsUniqueIndex ? 1 : 0);
offset = ws.put(offset, (uint32)mAttributes.size());
for (uint32 i = 0; i < mAttributes.size(); i++)
offset = ws.put(offset, mAttributes[i]->attributeId());
offset = ws.put(offset, (uint32)mMap.size());
uint32 keyPtrOffset = offset;
offset += AtomSize * mMap.size();
for (it = mMap.begin(); it != mMap.end(); it++) {
offset = ws.put(offset, it->second);
}
for (it = mMap.begin(); it != mMap.end(); it++) {
keyPtrOffset = ws.put(keyPtrOffset, offset);
offset = ws.put(offset, it->first.keySize());
offset = ws.put(offset, it->first.keySize(), it->first.keyData());
}
ws.put(sizeOffset, offset - sizeOffset);
return offset;
}