#include "MetaRecord.h"
#include <Security/trackingallocator.h>
MetaRecord::MetaRecord(CSSM_DB_RECORDTYPE inRecordType) :
mRecordType(inRecordType)
{
}
MetaRecord::MetaRecord(const CSSM_DB_RECORD_ATTRIBUTE_INFO &inInfo)
: mRecordType(inInfo.DataRecordType)
{
try
{
setRecordAttributeInfo(inInfo);
}
catch (...)
{
for_each_delete(mAttributeVector.begin(), mAttributeVector.end());
}
}
MetaRecord::MetaRecord(CSSM_DB_RECORDTYPE inRelationID,
uint32 inNumberOfAttributes,
const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *inAttributeInfo) :
mRecordType(inRelationID)
{
#if 0
if (inNumberOfAttributes == 0 || inAttributeInfo == NULL)
CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_NUM_ATTRIBUTES);
#endif
try {
for (uint32 anIndex = 0; anIndex < inNumberOfAttributes; anIndex++)
{
string aName;
if (inAttributeInfo[anIndex].AttributeName)
aName = string(inAttributeInfo[anIndex].AttributeName);
const CssmData *aNameID = NULL;
if (inAttributeInfo[anIndex].AttributeNameID.Length > 0)
aNameID = &CssmData::overlay(inAttributeInfo[anIndex].AttributeNameID);
uint32 aNumber = inAttributeInfo[anIndex].AttributeId;
createAttribute(
inAttributeInfo[anIndex].AttributeName ? &aName : NULL,
aNameID, aNumber,
inAttributeInfo[anIndex].DataType);
}
}
catch (...)
{
for_each_delete(mAttributeVector.begin(), mAttributeVector.end());
}
}
MetaRecord::~MetaRecord()
{
for_each_delete(mAttributeVector.begin(), mAttributeVector.end());
}
void
MetaRecord::setRecordAttributeInfo(const CSSM_DB_RECORD_ATTRIBUTE_INFO &inInfo)
{
#if 0
if (inInfo.NumberOfAttributes == 0 || inInfo.AttributeInfo == NULL)
CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_NUM_ATTRIBUTES);
#endif
for (uint32 anIndex = 0; anIndex < inInfo.NumberOfAttributes; anIndex++)
{
switch (inInfo.AttributeInfo[anIndex].AttributeNameFormat)
{
case CSSM_DB_ATTRIBUTE_NAME_AS_STRING:
{
string aName(inInfo.AttributeInfo[anIndex].Label.AttributeName);
createAttribute(&aName, nil, anIndex,
inInfo.AttributeInfo[anIndex].AttributeFormat);
break;
}
case CSSM_DB_ATTRIBUTE_NAME_AS_OID:
{
const CssmData &aNameID = CssmOid::overlay(inInfo.AttributeInfo[anIndex].Label.AttributeOID);
createAttribute(nil, &aNameID, anIndex,
inInfo.AttributeInfo[anIndex].AttributeFormat);
break;
}
case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER:
{
uint32 aNumber = inInfo.AttributeInfo[anIndex].Label.AttributeID;
createAttribute(nil, nil, aNumber,
inInfo.AttributeInfo[anIndex].AttributeFormat);
break;
}
default:
CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
break;
}
}
}
void
MetaRecord::createAttribute(const string *inAttributeName,
const CssmOid *inAttributeOID,
uint32 inAttributeID,
CSSM_DB_ATTRIBUTE_FORMAT inAttributeFormat)
{
uint32 anAttributeIndex = mAttributeVector.size();
bool aInsertedAttributeName = false;
bool aInsertedAttributeOID = false;
bool aInsertedAttributeID = false;
if (inAttributeName)
{
if (!mNameStringMap.insert(NameStringMap::value_type(*inAttributeName, anAttributeIndex)).second)
CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE);
aInsertedAttributeName = true;
}
try
{
if (inAttributeOID)
{
if (!mNameOIDMap.insert(NameOIDMap::value_type(*inAttributeOID, anAttributeIndex)).second)
CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE);
aInsertedAttributeOID = true;
}
if (!mNameIntMap.insert(NameIntMap::value_type(inAttributeID, anAttributeIndex)).second)
CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE);
aInsertedAttributeID = true;
mAttributeVector.push_back(MetaAttribute::create(inAttributeFormat,
anAttributeIndex, inAttributeID));
}
catch(...)
{
if (aInsertedAttributeName)
mNameStringMap.erase(*inAttributeName);
if (aInsertedAttributeOID)
mNameOIDMap.erase(*inAttributeOID);
if (inAttributeID)
mNameIntMap.erase(inAttributeID);
throw;
}
}
void
MetaRecord::packRecord(WriteSection &inWriteSection,
const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
const CssmData *inData) const
{
uint32 aDataSize;
if (inData)
aDataSize = inData->Length;
else
aDataSize = 0;
inWriteSection.put(OffsetDataSize, aDataSize);
uint32 anOffset = OffsetAttributeOffsets + AtomSize * mAttributeVector.size();
if (aDataSize)
anOffset = inWriteSection.put(anOffset, aDataSize, inData->Data);
vector<uint32> aNumValues(mAttributeVector.size(), ~0UL);
vector<CSSM_DATA_PTR> aValues(mAttributeVector.size());
uint32 anIndex;
if (inAttributes == NULL)
inWriteSection.put(OffsetSemanticInformation, 0);
else
{
inWriteSection.put(OffsetSemanticInformation, inAttributes->SemanticInformation);
anIndex = inAttributes->NumberOfAttributes;
if (anIndex > 0)
Required(inAttributes->AttributeData);
while (anIndex-- > 0)
{
CSSM_DB_ATTRIBUTE_DATA &anAttribute = inAttributes->AttributeData[anIndex];
uint32 anAttributeIndex = attributeIndex(anAttribute.Info);
if (anAttribute.Info.AttributeFormat != mAttributeVector[anAttributeIndex]->attributeFormat())
CssmError::throwMe(CSSMERR_DL_INCOMPATIBLE_FIELD_FORMAT);
if (aNumValues[anAttributeIndex] != ~0UL)
CssmError::throwMe(CSSMERR_DL_FIELD_SPECIFIED_MULTIPLE);
aNumValues[anAttributeIndex] = anAttribute.NumberOfValues;
aValues[anAttributeIndex] = anAttribute.Value;
}
}
for (anIndex = 0; anIndex < mAttributeVector.size(); ++anIndex)
{
const MetaAttribute &aMetaAttribute = *mAttributeVector[anIndex];
uint32 aNumberOfValues = aNumValues[anIndex];
if (aNumberOfValues == ~0UL)
aNumberOfValues = aDataSize == 0 ? 0 : aMetaAttribute.parse(*inData, aValues[anIndex]);
aMetaAttribute.packAttribute(inWriteSection, anOffset, aNumberOfValues, aValues[anIndex]);
}
inWriteSection.put(OffsetRecordSize, anOffset);
inWriteSection.size(anOffset);
}
inline void
MetaRecord::unpackAttribute(const ReadSection &inReadSection,
CssmAllocator &inAllocator,
CSSM_DB_ATTRIBUTE_DATA &inoutAttribute) const
{
const MetaAttribute &aMetaAttribute = metaAttribute(inoutAttribute.Info);
inoutAttribute.Info.AttributeFormat = aMetaAttribute.attributeFormat();
aMetaAttribute.unpackAttribute(inReadSection, inAllocator,
inoutAttribute.NumberOfValues,
inoutAttribute.Value);
}
void
MetaRecord::unpackRecord(const ReadSection &inReadSection,
CssmAllocator &inAllocator,
CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
CssmData *inoutData,
CSSM_QUERY_FLAGS inQueryFlags) const
{
TrackingAllocator anAllocator(inAllocator);
if (inoutData)
{
Range aDataRange = dataRange(inReadSection);
inoutData->Length = aDataRange.mSize;
inoutData->Data = inReadSection.allocCopyRange(aDataRange, anAllocator);
}
if (inoutAttributes)
{
inoutAttributes->DataRecordType = dataRecordType();
inoutAttributes->SemanticInformation = semanticInformation(inReadSection);
uint32 anIndex = inoutAttributes->NumberOfAttributes;
if (anIndex > 0 && inoutAttributes->AttributeData == NULL)
CssmError::throwMe(CSSM_ERRCODE_INVALID_POINTER);
while (anIndex-- > 0)
{
unpackAttribute(inReadSection, anAllocator,
inoutAttributes->AttributeData[anIndex]);
}
}
anAllocator.commit();
}
#ifndef NDEBUG
#define LOG_NAME_AS_STRING_FAIL
#endif
uint32
MetaRecord::attributeIndex(const CSSM_DB_ATTRIBUTE_INFO &inAttributeInfo) const
{
uint32 anIndex;
switch (inAttributeInfo.AttributeNameFormat)
{
case CSSM_DB_ATTRIBUTE_NAME_AS_STRING:
{
string aName(inAttributeInfo.Label.AttributeName);
assert(aName.size() < 500); NameStringMap::const_iterator it = mNameStringMap.find(aName);
if (it == mNameStringMap.end()) {
#ifdef LOG_NAME_AS_STRING_FAIL
printf("NAME_AS_STRING failure; attrName %s\n",
inAttributeInfo.Label.AttributeName);
for(it = mNameStringMap.begin();
it != mNameStringMap.end();
it++) {
printf("name %s val %ul\n", it->first.c_str(), it->second);
}
#endif
CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
}
anIndex = it->second;
break;
}
case CSSM_DB_ATTRIBUTE_NAME_AS_OID:
{
const CssmOid &aName = CssmOid::overlay(inAttributeInfo.Label.AttributeOID);
NameOIDMap::const_iterator it = mNameOIDMap.find(aName);
if (it == mNameOIDMap.end())
CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
anIndex = it->second;
break;
}
case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER:
{
uint32 aName = inAttributeInfo.Label.AttributeID;
NameIntMap::const_iterator it = mNameIntMap.find(aName);
if (it == mNameIntMap.end())
CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
anIndex = it->second;
break;
}
default:
CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME);
break;
}
return anIndex;
}
const MetaAttribute &
MetaRecord::metaAttribute(const CSSM_DB_ATTRIBUTE_INFO &inAttributeInfo) const
{
return *mAttributeVector[attributeIndex(inAttributeInfo)];
}
void
MetaRecord::updateRecord(const ReadSection &inReadSection,
WriteSection &inWriteSection,
const CssmDbRecordAttributeData *inAttributes,
const CssmData *inData,
CSSM_DB_MODIFY_MODE inModifyMode) const
{
TrackingAllocator anAllocator(CssmAllocator::standard());
uint32 aDataSize;
const uint8 *aDataData = NULL;
if (inData)
{
aDataSize = inData->Length;
aDataData = inData->Data;
}
else
{
Range aDataRange = dataRange(inReadSection);
aDataSize = aDataRange.mSize;
if (aDataSize)
aDataData = inReadSection.range(aDataRange);
}
uint32 anOffset = OffsetAttributeOffsets + AtomSize * mAttributeVector.size();
inWriteSection.put(OffsetDataSize, aDataSize);
if (aDataSize)
anOffset = inWriteSection.put(anOffset, aDataSize, aDataData);
auto_array<CssmDbAttributeData> attributeData(mAttributeVector.size());
for (uint32 anAttributeIndex = mAttributeVector.size(); anAttributeIndex-- > 0; )
{
const MetaAttribute &attribute = *mAttributeVector[anAttributeIndex];
attribute.unpackAttribute(inReadSection, anAllocator,
attributeData[anAttributeIndex].NumberOfValues,
attributeData[anAttributeIndex].Value);
}
uint32 oldSemanticInformation = semanticInformation(inReadSection);
if (inAttributes == NULL)
{
if (inModifyMode != CSSM_DB_MODIFY_ATTRIBUTE_NONE)
CssmError::throwMe(CSSMERR_DL_INVALID_MODIFY_MODE);
}
else {
uint32 inSemanticInformation = inAttributes ? inAttributes->SemanticInformation : 0;
if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_ADD)
oldSemanticInformation |= inSemanticInformation;
else if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_DELETE)
oldSemanticInformation &= ~inSemanticInformation;
else if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_REPLACE)
oldSemanticInformation = inSemanticInformation;
uint32 anIndex = inAttributes->NumberOfAttributes;
if (anIndex > 0)
Required(inAttributes->AttributeData);
while (anIndex-- > 0) {
const CssmDbAttributeData &anAttribute = inAttributes->at(anIndex);
uint32 anAttributeIndex = attributeIndex(anAttribute.info());
if (anAttribute.format() != mAttributeVector[anAttributeIndex]->attributeFormat())
CssmError::throwMe(CSSMERR_DL_INCOMPATIBLE_FIELD_FORMAT);
CssmDbAttributeData &oldAttribute = attributeData[anAttributeIndex];
if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_ADD)
oldAttribute.add(anAttribute, anAllocator);
else if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_DELETE)
{
if (anAttribute.size() == 0)
oldAttribute.deleteValues(anAllocator);
else
oldAttribute.deleteValues(anAttribute, anAllocator);
}
else if (inModifyMode == CSSM_DB_MODIFY_ATTRIBUTE_REPLACE)
{
oldAttribute.deleteValues(anAllocator);
if (anAttribute.size() > 0)
oldAttribute.add(anAttribute, anAllocator);
else
;
}
}
}
inWriteSection.put(OffsetSemanticInformation, oldSemanticInformation);
for (uint32 anIndex = 0; anIndex < mAttributeVector.size(); ++anIndex)
{
const MetaAttribute &metaAttribute = *mAttributeVector[anIndex];
metaAttribute.packAttribute(inWriteSection, anOffset,
attributeData[anIndex].NumberOfValues,
attributeData[anIndex].Value);
}
inWriteSection.put(OffsetRecordSize, anOffset);
inWriteSection.size(anOffset);
}