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.
20 // AppleDatabase.cpp - Description t.b.d.
22 #include "AppleDatabase.h"
23 #include <Security/DatabaseSession.h>
24 #include <Security/DbContext.h>
25 #include <Security/cssmdb.h>
26 #include <Security/cssmapple.h>
27 #include <Security/trackingallocator.h>
34 Table::Table(const ReadSection
&inTableSection
) :
35 mMetaRecord(inTableSection
[OffsetId
]),
36 mTableSection(inTableSection
),
37 mRecordsCount(inTableSection
[OffsetRecordsCount
]),
38 mFreeListHead(inTableSection
[OffsetFreeListHead
]),
39 mRecordNumbersCount(inTableSection
[OffsetRecordNumbersCount
])
41 // can't easily initialize indexes here, since meta record is incomplete
42 // until much later... see DbVersion::open()
47 for_each_map_delete(mIndexMap
.begin(), mIndexMap
.end());
51 Table::readIndexSection()
53 uint32 indexSectionOffset
= mTableSection
.at(OffsetIndexesOffset
);
55 uint32 numIndexes
= mTableSection
.at(indexSectionOffset
+ AtomSize
);
57 for (uint32 i
= 0; i
< numIndexes
; i
++) {
58 uint32 indexOffset
= mTableSection
.at(indexSectionOffset
+ (i
+ 2) * AtomSize
);
59 ReadSection
indexSection(mTableSection
.subsection(indexOffset
));
61 auto_ptr
<DbConstIndex
> index(new DbConstIndex(*this, indexSection
));
62 mIndexMap
.insert(ConstIndexMap::value_type(index
->indexId(), index
.get()));
68 Table::createCursor(const CSSM_QUERY
*inQuery
, const DbVersion
&inDbVersion
) const
70 // if an index matches the query, return a cursor which uses the index
72 ConstIndexMap::const_iterator it
;
75 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
76 if (it
->second
->matchesQuery(*inQuery
, queryKey
)) {
77 IndexCursor
*cursor
= new IndexCursor(queryKey
, inDbVersion
, *this, it
->second
);
81 // otherwise, return a cursor that iterates over all table records
83 return new LinearCursor(inQuery
, inDbVersion
, *this);
87 Table::getRecordSection(uint32 inRecordNumber
) const
89 if (inRecordNumber
>= mRecordNumbersCount
)
90 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID
);
92 uint32 aRecordOffset
= mTableSection
[OffsetRecordNumbers
+ AtomSize
95 // Check if this RecordNumber has been deleted.
96 if (aRecordOffset
& 1 || aRecordOffset
== 0)
97 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
99 return MetaRecord::readSection(mTableSection
, aRecordOffset
);
103 Table::getRecord(const RecordId
&inRecordId
,
104 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
106 CssmAllocator
&inAllocator
) const
108 const ReadSection aRecordSection
= getRecordSection(inRecordId
.mRecordNumber
);
109 const RecordId aRecordId
= MetaRecord::unpackRecordId(aRecordSection
);
111 // Make sure the RecordNumber matches that in the RecordId we just retrived.
112 if (aRecordId
.mRecordNumber
!= inRecordId
.mRecordNumber
)
113 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
115 if (aRecordId
.mCreateVersion
!= inRecordId
.mCreateVersion
)
116 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID
);
118 // XXX Figure out which value to pass for inQueryFlags (5th) argument
119 mMetaRecord
.unpackRecord(aRecordSection
, inAllocator
, inoutAttributes
,
125 Table::popFreeList(uint32
&aFreeListHead
) const
127 assert(aFreeListHead
| 1);
128 uint32 anOffset
= aFreeListHead
^ 1;
129 uint32 aRecordNumber
= (anOffset
- OffsetRecordNumbers
) / AtomSize
;
130 aFreeListHead
= mTableSection
[anOffset
];
131 return aRecordNumber
;
135 Table::getRecordsSection() const
137 return mTableSection
.subsection(mTableSection
[OffsetRecords
]);
141 Table::matchesTableId(Id inTableId
) const
143 Id anId
= mMetaRecord
.dataRecordType();
144 if (inTableId
== CSSM_DL_DB_RECORD_ANY
) // All non schema tables.
145 return !(CSSM_DB_RECORDTYPE_SCHEMA_START
<= anId
146 && anId
< CSSM_DB_RECORDTYPE_SCHEMA_END
);
148 if (inTableId
== CSSM_DL_DB_RECORD_ALL_KEYS
) // All key tables.
149 return (anId
== CSSM_DL_DB_RECORD_PUBLIC_KEY
150 || anId
== CSSM_DL_DB_RECORD_PRIVATE_KEY
151 || anId
== CSSM_DL_DB_RECORD_SYMMETRIC_KEY
);
153 return inTableId
== anId
; // Only if exact match.
160 ModifiedTable::ModifiedTable(const Table
*inTable
) :
163 mRecordNumberCount(inTable
->recordNumberCount()),
164 mFreeListHead(inTable
->freeListHead()),
169 ModifiedTable::ModifiedTable(MetaRecord
*inMetaRecord
) :
171 mNewMetaRecord(inMetaRecord
),
172 mRecordNumberCount(0),
178 ModifiedTable::~ModifiedTable()
180 for_each_map_delete(mIndexMap
.begin(), mIndexMap
.end());
181 for_each_map_delete(mInsertedMap
.begin(), mInsertedMap
.end());
183 delete mNewMetaRecord
;
187 ModifiedTable::deleteRecord(const RecordId
&inRecordId
)
191 uint32 aRecordNumber
= inRecordId
.mRecordNumber
;
193 // remove the record from all the indexes
194 MutableIndexMap::iterator it
;
195 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
196 it
->second
->removeRecord(aRecordNumber
);
198 InsertedMap::iterator anIt
= mInsertedMap
.find(inRecordId
.mRecordNumber
);
199 if (anIt
== mInsertedMap
.end())
201 // If we have no old table than this record can not exist yet.
203 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
205 const RecordId aRecordId
= MetaRecord::unpackRecordId(mTable
->getRecordSection(aRecordNumber
));
206 if (aRecordId
.mRecordVersion
!= inRecordId
.mRecordVersion
)
207 CssmError::throwMe(CSSMERR_DL_RECORD_MODIFIED
);
209 // Schedule the record for deletion
210 if (!mDeletedSet
.insert(aRecordNumber
).second
)
211 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // It was already deleted
215 const RecordId aRecordId
= MetaRecord::unpackRecordId(*anIt
->second
);
216 if (aRecordId
.mCreateVersion
!= inRecordId
.mCreateVersion
)
217 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
219 if (aRecordId
.mRecordVersion
!= inRecordId
.mRecordVersion
)
220 CssmError::throwMe(CSSMERR_DL_RECORD_MODIFIED
);
222 // Remove the inserted (but uncommited) record. It should already be in mDeletedSet
223 // if it existed previously in mTable.
224 mInsertedMap
.erase(anIt
);
230 ModifiedTable::insertRecord(AtomicFile::VersionId inVersionId
,
231 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
232 const CssmData
*inData
)
236 auto_ptr
<WriteSection
> aWriteSection(new WriteSection());
237 getMetaRecord().packRecord(*aWriteSection
, inAttributes
, inData
);
238 uint32 aRecordNumber
= nextRecordNumber();
240 // add the record to all the indexes; this will throw if the new record
241 // violates a unique index
242 MutableIndexMap::iterator it
;
243 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
244 it
->second
->insertRecord(aRecordNumber
, *(aWriteSection
.get()));
246 // schedule the record for insertion
247 RecordId
aRecordId(aRecordNumber
, inVersionId
);
248 MetaRecord::packRecordId(aRecordId
, *aWriteSection
);
249 mInsertedMap
.insert(InsertedMap::value_type(aRecordNumber
, aWriteSection
.get()));
251 aWriteSection
.release();
257 ModifiedTable::updateRecord(const RecordId
&inRecordId
,
258 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
259 const CssmData
*inData
,
260 CSSM_DB_MODIFY_MODE inModifyMode
)
264 uint32 aRecordNumber
= inRecordId
.mRecordNumber
;
265 InsertedMap::iterator anIt
= mInsertedMap
.find(inRecordId
.mRecordNumber
);
267 // aReUpdate is true iff we are updating an already updated record.
268 bool aReUpdate
= anIt
!= mInsertedMap
.end();
270 // If we are not re-updating and there is no old table than this record does not exist yet.
271 if (!aReUpdate
&& !mTable
)
272 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
274 const ReadSection
&anOldDbRecord
= aReUpdate
? *anIt
->second
: mTable
->getRecordSection(aRecordNumber
);
275 const RecordId aRecordId
= MetaRecord::unpackRecordId(anOldDbRecord
);
277 // Did someone else delete the record we are trying to update.
278 if (aRecordId
.mCreateVersion
!= inRecordId
.mCreateVersion
)
279 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
281 // Is the record we that our update is based on current?
282 if (aRecordId
.mRecordVersion
!= inRecordId
.mRecordVersion
)
283 CssmError::throwMe(CSSMERR_DL_STALE_UNIQUE_RECORD
);
285 // Update the actual packed record.
286 auto_ptr
<WriteSection
> aDbRecord(new WriteSection());
287 getMetaRecord().updateRecord(anOldDbRecord
, *aDbRecord
,
288 CssmDbRecordAttributeData::overlay(inAttributes
), inData
, inModifyMode
);
291 // Bump the RecordVersion of this record.
292 RecordId
aNewRecordId(aRecordNumber
, inRecordId
.mCreateVersion
, inRecordId
.mRecordVersion
+ 1);
293 // Store the RecordVersion in the packed aDbRecord.
294 MetaRecord::packRecordId(aNewRecordId
, *aDbRecord
);
296 if (!aReUpdate
&& !mDeletedSet
.insert(aRecordNumber
).second
)
297 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // Record was already in mDeletedSet
301 // remove the original record from all the indexes
302 MutableIndexMap::iterator it
;
303 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
304 it
->second
->removeRecord(aRecordNumber
);
306 // add the updated record to all the indexes; this will throw if the new record
307 // violates a unique index
308 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
309 it
->second
->insertRecord(aRecordNumber
, *(aDbRecord
.get()));
311 mInsertedMap
.insert(InsertedMap::value_type(aRecordNumber
, aDbRecord
.get()));
317 mDeletedSet
.erase(aRecordNumber
);
325 ModifiedTable::nextRecordNumber()
327 // If we still have unused free records in mTable get the next one.
329 return mTable
->popFreeList(mFreeListHead
);
331 // Bump up the mRecordNumberCount so we don't reuse the same one.
332 return mRecordNumberCount
++;
336 ModifiedTable::recordNumberCount() const
338 uint32 anOldMax
= !mTable
? 0 : mTable
->recordNumberCount() - 1;
339 uint32 anInsertedMax
= mInsertedMap
.empty() ? 0 : mInsertedMap
.rbegin()->first
;
341 DeletedSet::reverse_iterator anIt
= mDeletedSet
.rbegin();
342 DeletedSet::reverse_iterator anEnd
= mDeletedSet
.rend();
343 for (; anIt
!= anEnd
; anIt
++)
345 if (*anIt
!= anOldMax
|| anOldMax
<= anInsertedMax
)
350 return max(anOldMax
,anInsertedMax
) + 1;
354 ModifiedTable::getMetaRecord() const
356 return mNewMetaRecord
? *mNewMetaRecord
: mTable
->getMetaRecord();
359 // prepare to modify the table
362 ModifiedTable::modifyTable()
365 createMutableIndexes();
370 // create mutable indexes from the read-only indexes in the underlying table
373 ModifiedTable::createMutableIndexes()
378 Table::ConstIndexMap::const_iterator it
;
379 for (it
= mTable
->mIndexMap
.begin(); it
!= mTable
->mIndexMap
.end(); it
++) {
380 auto_ptr
<DbMutableIndex
> mutableIndex(new DbMutableIndex(*it
->second
));
381 mIndexMap
.insert(MutableIndexMap::value_type(it
->first
, mutableIndex
.get()));
382 mutableIndex
.release();
386 // find, and create if needed, an index with the given id
389 ModifiedTable::findIndex(uint32 indexId
, const MetaRecord
&metaRecord
, bool isUniqueIndex
)
391 MutableIndexMap::iterator it
= mIndexMap
.find(indexId
);
393 if (it
== mIndexMap
.end()) {
394 // create the new index
395 auto_ptr
<DbMutableIndex
> index(new DbMutableIndex(metaRecord
, indexId
, isUniqueIndex
));
396 it
= mIndexMap
.insert(MutableIndexMap::value_type(indexId
, index
.get())).first
;
404 ModifiedTable::writeIndexSection(WriteSection
&tableSection
, uint32 offset
)
406 MutableIndexMap::iterator it
;
408 tableSection
.put(Table::OffsetIndexesOffset
, offset
);
410 // leave room for the size, to be written later
411 uint32 indexSectionOffset
= offset
;
414 offset
= tableSection
.put(offset
, mIndexMap
.size());
416 // leave room for the array of offsets to the indexes
417 uint32 indexOffsetOffset
= offset
;
418 offset
+= mIndexMap
.size() * AtomSize
;
421 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++) {
422 indexOffsetOffset
= tableSection
.put(indexOffsetOffset
, offset
);
423 offset
= it
->second
->writeIndex(tableSection
, offset
);
426 // write the total index section size
427 tableSection
.put(indexSectionOffset
, offset
- indexSectionOffset
);
433 ModifiedTable::writeTable(AtomicFile
&inAtomicFile
, uint32 inSectionOffset
)
435 if (mTable
&& !mIsModified
) {
436 // the table has not been modified, so we can just dump the old table
437 // section into the new database
439 const ReadSection
&tableSection
= mTable
->getTableSection();
440 uint32 tableSize
= tableSection
.at(Table::OffsetSize
);
442 inAtomicFile
.write(AtomicFile::FromStart
, inSectionOffset
,
443 tableSection
.range(Range(0, tableSize
)), tableSize
);
445 return inSectionOffset
+ tableSize
;
448 // We should have an old mTable or a mNewMetaRecord but not both.
449 assert(mTable
!= nil
^ mNewMetaRecord
!= nil
);
450 const MetaRecord
&aNewMetaRecord
= getMetaRecord();
452 uint32 aRecordsCount
= 0;
453 uint32 aRecordNumbersCount
= recordNumberCount();
454 uint32 aRecordsOffset
= Table::OffsetRecordNumbers
+ AtomSize
* aRecordNumbersCount
;
455 WriteSection
aTableSection(CssmAllocator::standard(), aRecordsOffset
);
456 aTableSection
.size(aRecordsOffset
);
457 aTableSection
.put(Table::OffsetId
, aNewMetaRecord
.dataRecordType());
458 aTableSection
.put(Table::OffsetRecords
, aRecordsOffset
);
459 aTableSection
.put(Table::OffsetRecordNumbersCount
, aRecordNumbersCount
);
461 uint32 anOffset
= inSectionOffset
+ aRecordsOffset
;
465 // XXX Handle schema changes in the future.
466 assert(mNewMetaRecord
== nil
);
468 // We have a modified old table so copy all non deleted records
469 // The code below is rather elaborate, but this is because it attempts
470 // to copy large ranges of non deleted records with single calls
471 // to AtomicFile::write()
472 uint32 anOldRecordsCount
= mTable
->getRecordsCount();
473 ReadSection aRecordsSection
= mTable
->getRecordsSection();
474 uint32 aReadOffset
= 0; // Offset of current record
475 uint32 aWriteOffset
= aRecordsOffset
; // Offset for current write record
476 uint32 aBlockStart
= aReadOffset
; // Starting point for read
477 uint32 aBlockSize
= 0; // Size of block to read
478 for (uint32 aRecord
= 0; aRecord
< anOldRecordsCount
; aRecord
++)
480 ReadSection aRecordSection
= MetaRecord::readSection(aRecordsSection
, aReadOffset
);
481 uint32 aRecordNumber
= MetaRecord::unpackRecordNumber(aRecordSection
);
482 uint32 aRecordSize
= aRecordSection
.size();
483 aReadOffset
+= aRecordSize
;
484 if (mDeletedSet
.find(aRecordNumber
) == mDeletedSet
.end())
486 // This record has not been deleted. Register the offset
487 // at which it will be in the new file in aTableSection.
488 aTableSection
.put(Table::OffsetRecordNumbers
489 + AtomSize
* aRecordNumber
,
491 aWriteOffset
+= aRecordSize
;
492 aBlockSize
+= aRecordSize
;
494 // XXX update all indexes being created.
498 // The current record has been deleted. Copy all records up
499 // to but not including the current one to the new file.
502 inAtomicFile
.write(AtomicFile::FromStart
, anOffset
,
503 aRecordsSection
.range(Range(aBlockStart
,
506 anOffset
+= aBlockSize
;
509 // Set the start of the next block to the start of the next
510 // record, and the size of the block to 0.
511 aBlockStart
= aReadOffset
;
513 } // if (mDeletedSet..)
514 } // for (aRecord...)
516 // Copy all records that have not yet been copied to the new file.
519 inAtomicFile
.write(AtomicFile::FromStart
, anOffset
,
520 aRecordsSection
.range(Range(aBlockStart
,
523 anOffset
+= aBlockSize
;
527 // Now add all inserted records to the table.
528 InsertedMap::const_iterator anIt
= mInsertedMap
.begin();
529 InsertedMap::const_iterator anEnd
= mInsertedMap
.end();
530 // Iterate over all inserted objects.
531 for (; anIt
!= anEnd
; anIt
++)
533 // Write out each inserted/modified record
534 const WriteSection
&aRecord
= *anIt
->second
;
535 uint32 aRecordNumber
= anIt
->first
;
536 // Put offset relative to start of this table in recordNumber array.
537 aTableSection
.put(Table::OffsetRecordNumbers
+ AtomSize
* aRecordNumber
,
538 anOffset
- inSectionOffset
);
539 inAtomicFile
.write(AtomicFile::FromStart
, anOffset
,
540 aRecord
.address(), aRecord
.size());
541 anOffset
+= aRecord
.size();
543 // XXX update all indexes being created.
546 // Reconstruct the freelist (this is O(N) where N is the number of recordNumbers)
547 // We could implement it faster by using the old freelist and skipping the records
548 // that have been inserted. However building the freelist for the newly used
549 // recordNumbers (not in mTable) would look like the code below anyway (starting
550 // from mTable->recordNumberCount()).
551 // The first part of this would be O(M Log(N)) (where M is the old number of
552 // free records, and N is the number of newly inserted records)
553 // The second part would be O(N) where N is the currently max RecordNumber
554 // in use - the old max RecordNumber in use.
555 uint32 aFreeListHead
= 0; // Link to previous free record
556 for (uint32 aRecordNumber
= 0; aRecordNumber
< aRecordNumbersCount
; aRecordNumber
++)
558 // Make the freelist a list of all records with 0 offset (non existing).
559 if (!aTableSection
.at(Table::OffsetRecordNumbers
+ AtomSize
* aRecordNumber
))
561 aTableSection
.put(Table::OffsetRecordNumbers
562 + AtomSize
* aRecordNumber
,
564 // Make aFreeListHead point to the previous free recordNumber slot in the table.
565 aFreeListHead
= (Table::OffsetRecordNumbers
+ AtomSize
* aRecordNumber
) | 1;
568 aTableSection
.put(Table::OffsetFreeListHead
, aFreeListHead
);
570 anOffset
-= inSectionOffset
;
572 // Write out indexes, which are part of the table section
575 uint32 indexOffset
= anOffset
;
576 anOffset
= writeIndexSection(aTableSection
, anOffset
);
577 inAtomicFile
.write(AtomicFile::FromStart
, inSectionOffset
+ indexOffset
,
578 aTableSection
.address() + indexOffset
, anOffset
- indexOffset
);
581 // Set the section size and recordCount.
582 aTableSection
.put(Table::OffsetSize
, anOffset
);
583 aTableSection
.put(Table::OffsetRecordsCount
, aRecordsCount
);
585 // Write out aTableSection header.
586 inAtomicFile
.write(AtomicFile::FromStart
, inSectionOffset
,
587 aTableSection
.address(), aTableSection
.size());
589 return anOffset
+ inSectionOffset
;
598 // Attribute definitions
600 static const CSSM_DB_ATTRIBUTE_INFO RelationID
=
602 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
604 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
606 static const CSSM_DB_ATTRIBUTE_INFO RelationName
=
608 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
610 CSSM_DB_ATTRIBUTE_FORMAT_STRING
612 static const CSSM_DB_ATTRIBUTE_INFO AttributeID
=
614 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
616 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
618 static const CSSM_DB_ATTRIBUTE_INFO AttributeNameFormat
=
620 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
621 {"AttributeNameFormat"},
622 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
624 static const CSSM_DB_ATTRIBUTE_INFO AttributeName
=
626 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
628 CSSM_DB_ATTRIBUTE_FORMAT_STRING
630 static const CSSM_DB_ATTRIBUTE_INFO AttributeNameID
=
632 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
634 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
636 static const CSSM_DB_ATTRIBUTE_INFO AttributeFormat
=
638 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
640 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
642 static const CSSM_DB_ATTRIBUTE_INFO IndexID
=
644 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
646 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
648 static const CSSM_DB_ATTRIBUTE_INFO IndexType
=
650 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
652 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
654 static const CSSM_DB_ATTRIBUTE_INFO IndexedDataLocation
=
656 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
657 {"IndexedDataLocation"},
658 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
660 static const CSSM_DB_ATTRIBUTE_INFO ModuleID
=
662 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
664 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
666 static const CSSM_DB_ATTRIBUTE_INFO AddinVersion
=
668 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
670 CSSM_DB_ATTRIBUTE_FORMAT_STRING
672 static const CSSM_DB_ATTRIBUTE_INFO SSID
=
674 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
676 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
678 static const CSSM_DB_ATTRIBUTE_INFO SubserviceType
=
680 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
682 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
685 #define ATTRIBUTE(type, name) \
686 { CSSM_DB_ATTRIBUTE_NAME_AS_STRING, { #name }, CSSM_DB_ATTRIBUTE_FORMAT_ ## type }
688 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaRelations
[] =
690 //RelationID, RelationName
691 ATTRIBUTE(UINT32
, RelationID
),
692 ATTRIBUTE(STRING
, RelationName
)
695 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaAttributes
[] =
697 //RelationID, AttributeID,
698 //AttributeNameFormat, AttributeName, AttributeNameID,
700 ATTRIBUTE(UINT32
, RelationID
),
701 ATTRIBUTE(UINT32
, AttributeID
),
702 ATTRIBUTE(UINT32
, AttributeNameFormat
),
703 ATTRIBUTE(STRING
, AttributeName
),
704 ATTRIBUTE(BLOB
, AttributeNameID
),
705 ATTRIBUTE(UINT32
, AttributeFormat
)
708 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaIndexes
[] =
710 ATTRIBUTE(UINT32
, RelationID
),
711 ATTRIBUTE(UINT32
, IndexID
),
712 ATTRIBUTE(UINT32
, AttributeID
),
713 ATTRIBUTE(UINT32
, IndexType
),
714 ATTRIBUTE(UINT32
, IndexedDataLocation
)
715 //RelationID, IndexID, AttributeID,
716 //IndexType, IndexedDataLocation
719 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaParsingModule
[] =
721 ATTRIBUTE(UINT32
, RelationID
),
722 ATTRIBUTE(UINT32
, AttributeID
),
723 ATTRIBUTE(BLOB
, ModuleID
),
724 ATTRIBUTE(STRING
, AddinVersion
),
725 ATTRIBUTE(UINT32
, SSID
),
726 ATTRIBUTE(UINT32
, SubserviceType
)
727 //RelationID, AttributeID,
728 //ModuleID, AddinVersion, SSID, SubserviceType
736 DbVersion::DbVersion(AtomicFile
&inDatabaseFile
,
737 const AppleDatabase
&db
) :
738 mDatabase(reinterpret_cast<const uint8
*>(NULL
), 0), mDatabaseFile(&inDatabaseFile
),
741 const uint8
*aFileAddress
;
743 mVersionId
= mDatabaseFile
->enterRead(aFileAddress
, aLength
);
744 mDatabase
= ReadSection(aFileAddress
, aLength
);
748 DbVersion::~DbVersion()
752 for_each_map_delete(mTableMap
.begin(), mTableMap
.end());
754 mDatabaseFile
->exitRead(mVersionId
);
760 DbVersion::isDirty() const
763 return mDatabaseFile
->isDirty(mVersionId
);
773 // This is the oposite of DbModifier::commit()
774 const ReadSection aHeaderSection
= mDatabase
.subsection(HeaderOffset
,
776 if (aHeaderSection
.at(OffsetMagic
) != HeaderMagic
)
777 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
779 // We currently only support one version. If we support additional
780 // file format versions in the future fix this.
781 uint32 aVersion
= aHeaderSection
.at(OffsetVersion
);
782 if (aVersion
!= HeaderVersion
)
783 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
785 //const ReadSection anAuthSection =
786 // mDatabase.subsection(HeaderOffset + aHeaderSection.at(OffsetAuthOffset));
787 // XXX Do something with anAuthSection.
789 uint32 aSchemaOffset
= aHeaderSection
.at(OffsetSchemaOffset
);
790 const ReadSection aSchemaSection
=
791 mDatabase
.subsection(HeaderOffset
+ aSchemaOffset
);
793 uint32 aSchemaSize
= aSchemaSection
[OffsetSchemaSize
];
794 // Make sure that the given range exists.
795 aSchemaSection
.subsection(0, aSchemaSize
);
796 uint32 aTableCount
= aSchemaSection
[OffsetTablesCount
];
798 // Assert that the size of this section is big enough.
799 if (aSchemaSize
< OffsetTables
+ AtomSize
* aTableCount
)
800 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
802 for (uint32 aTableNumber
= 0; aTableNumber
< aTableCount
;
805 uint32 aTableOffset
= aSchemaSection
.at(OffsetTables
+ AtomSize
807 // XXX Set the size boundary on aTableSection.
808 const ReadSection aTableSection
=
809 aSchemaSection
.subsection(aTableOffset
);
810 auto_ptr
<Table
> aTable(new Table(aTableSection
));
811 Table::Id aTableId
= aTable
->getMetaRecord().dataRecordType();
812 mTableMap
.insert(TableMap::value_type(aTableId
, aTable
.get()));
816 // Fill in the schema for the meta tables.
818 findTable(mDb
.schemaRelations
.DataRecordType
).getMetaRecord().
819 setRecordAttributeInfo(mDb
.schemaRelations
);
820 findTable(mDb
.schemaIndexes
.DataRecordType
).getMetaRecord().
821 setRecordAttributeInfo(mDb
.schemaIndexes
);
822 findTable(mDb
.schemaParsingModule
.DataRecordType
).getMetaRecord().
823 setRecordAttributeInfo(mDb
.schemaParsingModule
);
825 // OK, we have created all the tables in the tableMap. Now
826 // lets read the schema and proccess it accordingly.
827 // Iterate over all schema records.
828 Table
&aTable
= findTable(mDb
.schemaAttributes
.DataRecordType
);
829 aTable
.getMetaRecord().setRecordAttributeInfo(mDb
.schemaAttributes
);
830 uint32 aRecordsCount
= aTable
.getRecordsCount();
831 ReadSection aRecordsSection
= aTable
.getRecordsSection();
832 uint32 aReadOffset
= 0;
833 const MetaRecord
&aMetaRecord
= aTable
.getMetaRecord();
835 CSSM_DB_ATTRIBUTE_DATA aRelationIDData
=
841 CSSM_DB_ATTRIBUTE_DATA aAttributeIDData
=
847 CSSM_DB_ATTRIBUTE_DATA aAttributeNameFormatData
=
853 CSSM_DB_ATTRIBUTE_DATA aAttributeNameData
=
859 CSSM_DB_ATTRIBUTE_DATA aAttributeNameIDData
=
865 CSSM_DB_ATTRIBUTE_DATA aAttributeFormatData
=
871 CSSM_DB_ATTRIBUTE_DATA aRecordAttributes
[] =
875 aAttributeNameFormatData
,
877 aAttributeNameIDData
,
880 CSSM_DB_RECORD_ATTRIBUTE_DATA aRecordAttributeData
=
882 aMetaRecord
.dataRecordType(),
884 sizeof(aRecordAttributes
) / sizeof(CSSM_DB_ATTRIBUTE_DATA
),
887 CssmDbRecordAttributeData
&aRecordData
= CssmDbRecordAttributeData::overlay(aRecordAttributeData
);
889 TrackingAllocator
recordAllocator(CssmAllocator::standard());
890 for (uint32 aRecord
= 0; aRecord
!= aRecordsCount
; aRecord
++)
892 ReadSection aRecordSection
= MetaRecord::readSection(aRecordsSection
, aReadOffset
);
893 uint32 aRecordSize
= aRecordSection
.size();
894 aReadOffset
+= aRecordSize
;
899 aMetaRecord
.unpackRecord(aRecordSection
, recordAllocator
,
900 &aRecordAttributeData
, NULL
, 0);
901 // Create the attribute coresponding to this entry
902 if (aRecordData
[0].size() != 1 || aRecordData
[0].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
)
903 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
904 uint32 aRelationId
= aRecordData
[0];
906 // Skip the schema relations for the meta tables themselves.
907 // FIXME: this hard-wires the meta-table relation IDs to be
908 // within {CSSM_DB_RECORDTYPE_SCHEMA_START...
909 // CSSM_DB_RECORDTYPE_SCHEMA_END} (which is {0..4}).
910 // Bogus - the MDS schema relation IDs start at
911 // CSSM_DB_RELATIONID_MDS_START which is 0x40000000.
912 // Ref. Radar 2817921.
913 if (CSSM_DB_RECORDTYPE_SCHEMA_START
<= aRelationId
&& aRelationId
< CSSM_DB_RECORDTYPE_SCHEMA_END
)
916 // Get the MetaRecord corresponding to the specified RelationId
917 MetaRecord
&aMetaRecord
= findTable(aRelationId
).getMetaRecord();
919 if (aRecordData
[1].size() != 1
920 || aRecordData
[1].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
921 || aRecordData
[2].size() != 1
922 || aRecordData
[2].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
923 || aRecordData
[5].size() != 1
924 || aRecordData
[5].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
)
925 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
927 uint32 anAttributeId
= aRecordData
[1];
928 uint32 anAttributeNameFormat
= aRecordData
[2];
929 uint32 anAttributeFormat
= aRecordData
[5];
930 auto_ptr
<string
> aName
;
931 const CssmData
*aNameID
= NULL
;
933 if (aRecordData
[3].size() == 1)
935 if (aRecordData
[3].format() != CSSM_DB_ATTRIBUTE_FORMAT_STRING
)
936 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
938 auto_ptr
<string
> aName2(new string(static_cast<string
>(aRecordData
[3])));
942 if (aRecordData
[4].size() == 1)
944 if (aRecordData
[4].format() != CSSM_DB_ATTRIBUTE_FORMAT_BLOB
)
945 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
947 // @@@ Invoking conversion operator to CssmData & on aRecordData[4]
948 // And taking address of result.
949 aNameID
= &static_cast<CssmData
&>(aRecordData
[4]);
952 // Make sure that the attribute specified by anAttributeNameFormat is present.
953 switch (anAttributeNameFormat
)
955 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING
:
956 if (aRecordData
[3].size() != 1)
957 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
959 case CSSM_DB_ATTRIBUTE_NAME_AS_OID
:
960 if (aRecordData
[4].size() != 1)
961 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
963 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
:
966 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
969 // Create the attribute
970 aMetaRecord
.createAttribute(aName
.get(), aNameID
, anAttributeId
, anAttributeFormat
);
974 aRecordData
.deleteValues(CssmAllocator::standard());
978 aRecordData
.deleteValues(CssmAllocator::standard());
984 // initialize the indexes associated with each table
986 TableMap::iterator it
;
987 for (it
= mTableMap
.begin(); it
!= mTableMap
.end(); it
++)
988 it
->second
->readIndexSection();
993 for_each_map_delete(mTableMap
.begin(), mTableMap
.end());
1000 DbVersion::getRecord(Table::Id inTableId
, const RecordId
&inRecordId
,
1001 CSSM_DB_RECORD_ATTRIBUTE_DATA
*inoutAttributes
,
1002 CssmData
*inoutData
,
1003 CssmAllocator
&inAllocator
) const
1005 return findTable(inTableId
).getRecord(inRecordId
, inoutAttributes
,
1006 inoutData
, inAllocator
);
1010 DbVersion::createCursor(const CSSM_QUERY
*inQuery
) const
1012 // XXX We should add support for these special query types
1013 // By Creating a Cursor that iterates over multiple tables
1014 if (!inQuery
|| inQuery
->RecordType
== CSSM_DL_DB_RECORD_ANY
1015 || inQuery
->RecordType
== CSSM_DL_DB_RECORD_ALL_KEYS
)
1017 return new MultiCursor(inQuery
, *this);
1020 return findTable(inQuery
->RecordType
).createCursor(inQuery
, *this);
1024 DbVersion::findTable(Table::Id inTableId
) const
1026 TableMap::const_iterator it
= mTableMap
.find(inTableId
);
1027 if (it
== mTableMap
.end())
1028 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1033 DbVersion::findTable(Table::Id inTableId
)
1035 TableMap::iterator it
= mTableMap
.find(inTableId
);
1036 if (it
== mTableMap
.end())
1037 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1042 // Cursor implemetation
1050 // LinearCursor implemetation
1052 LinearCursor::LinearCursor(const CSSM_QUERY
*inQuery
, const DbVersion
&inDbVersion
,
1053 const Table
&inTable
) :
1054 mDbVersion(&inDbVersion
),
1055 mRecordsCount(inTable
.getRecordsCount()),
1057 mRecordsSection(inTable
.getRecordsSection()),
1059 mMetaRecord(inTable
.getMetaRecord())
1063 mConjunctive
= inQuery
->Conjunctive
;
1064 mQueryFlags
= inQuery
->QueryFlags
;
1065 // XXX Do something with inQuery->QueryLimits?
1066 uint32 aPredicatesCount
= inQuery
->NumSelectionPredicates
;
1067 mPredicates
.resize(aPredicatesCount
);
1070 for (uint32 anIndex
= 0; anIndex
< aPredicatesCount
; anIndex
++)
1072 CSSM_SELECTION_PREDICATE
&aPredicate
= inQuery
->SelectionPredicate
[anIndex
];
1073 mPredicates
[anIndex
] = new SelectionPredicate(mMetaRecord
, aPredicate
);
1078 for_each_delete(mPredicates
.begin(), mPredicates
.end());
1084 LinearCursor::~LinearCursor()
1086 for_each_delete(mPredicates
.begin(), mPredicates
.end());
1090 LinearCursor::next(Table::Id
&outTableId
,
1091 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
1092 CssmData
*inoutData
, CssmAllocator
&inAllocator
, RecordId
&recordId
)
1094 while (mRecord
++ < mRecordsCount
)
1096 ReadSection aRecordSection
= MetaRecord::readSection(mRecordsSection
, mReadOffset
);
1097 uint32 aRecordSize
= aRecordSection
.size();
1098 mReadOffset
+= aRecordSize
;
1100 PredicateVector::const_iterator anIt
= mPredicates
.begin();
1101 PredicateVector::const_iterator anEnd
= mPredicates
.end();
1105 // If there are no predicates we have a match.
1108 else if (mConjunctive
== CSSM_DB_OR
)
1110 // If mConjunctive is OR, the first predicate that returns
1111 // true indicates a match. Dropthough means no match
1113 for (; anIt
!= anEnd
; anIt
++)
1115 if ((*anIt
)->evaluate(aRecordSection
))
1122 else if (mConjunctive
== CSSM_DB_AND
|| mConjunctive
== CSSM_DB_NONE
)
1124 // If mConjunctive is AND (or NONE), the first predicate that returns
1125 // false indicates a mismatch. Dropthough means a match
1127 for (; anIt
!= anEnd
; anIt
++)
1129 if (!(*anIt
)->evaluate(aRecordSection
))
1138 // XXX Should be CSSMERR_DL_INVALID_QUERY (or CSSMERR_DL_INVALID_CONJUNTIVE).
1139 CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_QUERY
);
1144 // Get the actual record.
1145 mMetaRecord
.unpackRecord(aRecordSection
, inAllocator
,
1146 inoutAttributes
, inoutData
,
1148 outTableId
= mMetaRecord
.dataRecordType();
1149 recordId
= MetaRecord::unpackRecordId(aRecordSection
);
1161 IndexCursor::IndexCursor(DbQueryKey
*queryKey
, const DbVersion
&inDbVersion
,
1162 const Table
&table
, const DbConstIndex
*index
)
1163 : mQueryKey(queryKey
), mDbVersion(inDbVersion
), mTable(table
), mIndex(index
)
1165 index
->performQuery(*queryKey
, mBegin
, mEnd
);
1168 IndexCursor::~IndexCursor()
1170 // the query key will be deleted automatically, since it's an auto_ptr
1174 IndexCursor::next(Table::Id
&outTableId
,
1175 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes
,
1177 CssmAllocator
&inAllocator
, RecordId
&recordId
)
1182 ReadSection rs
= mIndex
->getRecordSection(mBegin
++);
1183 const MetaRecord
&metaRecord
= mTable
.getMetaRecord();
1185 outTableId
= metaRecord
.dataRecordType();
1186 metaRecord
.unpackRecord(rs
, inAllocator
, outAttributes
, outData
, 0);
1188 recordId
= MetaRecord::unpackRecordId(rs
);
1195 MultiCursor::MultiCursor(const CSSM_QUERY
*inQuery
, const DbVersion
&inDbVersion
) :
1196 mDbVersion(&inDbVersion
), mTableIterator(inDbVersion
.begin())
1199 mQuery
.reset(new CssmAutoQuery(*inQuery
));
1202 mQuery
.reset(new CssmAutoQuery());
1203 mQuery
->recordType(CSSM_DL_DB_RECORD_ANY
);
1207 MultiCursor::~MultiCursor()
1212 MultiCursor::next(Table::Id
&outTableId
,
1213 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
1214 CssmData
*inoutData
, CssmAllocator
&inAllocator
, RecordId
&recordId
)
1220 if (mTableIterator
== mDbVersion
->end())
1223 const Table
&aTable
= *mTableIterator
++;
1224 if (!aTable
.matchesTableId(mQuery
->recordType()))
1227 mCursor
.reset(aTable
.createCursor(mQuery
.get(), *mDbVersion
));
1230 if (mCursor
->next(outTableId
, inoutAttributes
, inoutData
, inAllocator
, recordId
))
1233 mCursor
.reset(NULL
);
1241 DbModifier::DbModifier(AtomicFile
&inAtomicFile
, const AppleDatabase
&db
) :
1244 mAtomicFile(inAtomicFile
),
1250 DbModifier::~DbModifier()
1254 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1262 const RefPointer
<const DbVersion
>
1263 DbModifier::getDbVersion()
1265 StLock
<Mutex
> _(mDbVersionLock
);
1266 if (mDbVersion
&& mDbVersion
->isDirty())
1269 if (mDbVersion
== NULL
)
1270 mDbVersion
= new DbVersion(mAtomicFile
, mDb
);
1276 DbModifier::createDatabase(const CSSM_DBINFO
&inDbInfo
,
1277 const CSSM_ACL_ENTRY_INPUT
*inInitialAclEntry
)
1279 // XXX This needs better locking. There is a possible race condition between
1280 // two concurrent creators. Or a writer/creator or a close/create etc.
1281 if (mWriting
|| !mModifiedTableMap
.empty())
1282 CssmError::throwMe(CSSMERR_DL_DATASTORE_ALREADY_EXISTS
);
1284 mVersionId
= mAtomicFile
.enterCreate(mFileRef
);
1287 // we need to create the meta tables first, because inserting tables
1288 // (including the meta tables themselves) relies on them being there
1289 createTable(new MetaRecord(mDb
.schemaRelations
));
1290 createTable(new MetaRecord(mDb
.schemaAttributes
));
1291 createTable(new MetaRecord(mDb
.schemaIndexes
));
1292 createTable(new MetaRecord(mDb
.schemaParsingModule
));
1294 // now add the meta-tables' schema to the meta tables themselves
1295 insertTableSchema(mDb
.schemaRelations
);
1296 insertTableSchema(mDb
.schemaAttributes
);
1297 insertTableSchema(mDb
.schemaIndexes
);
1298 insertTableSchema(mDb
.schemaParsingModule
);
1300 if (inInitialAclEntry
!= NULL
)
1302 //createACL(*inInitialAclEntry);
1305 if (inDbInfo
.NumberOfRecordTypes
== 0)
1307 if (inDbInfo
.RecordAttributeNames
== NULL
)
1308 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1309 if (inDbInfo
.RecordIndexes
== NULL
)
1310 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_INDEX
);
1311 if (inDbInfo
.DefaultParsingModules
== NULL
)
1312 CssmError::throwMe(CSSMERR_DL_INVALID_PARSING_MODULE
);
1314 for (uint32 anIndex
= 0; anIndex
< inDbInfo
.NumberOfRecordTypes
; anIndex
++)
1316 insertTable(CssmDbRecordAttributeInfo::overlay(inDbInfo
.RecordAttributeNames
[anIndex
]),
1317 &inDbInfo
.RecordIndexes
[anIndex
],
1318 &inDbInfo
.DefaultParsingModules
[anIndex
]);
1322 void DbModifier::openDatabase()
1324 commit(); // XXX Requires write lock.
1328 void DbModifier::closeDatabase()
1330 commit(); // XXX Requires write lock.
1331 StLock
<Mutex
> _(mDbVersionLock
);
1335 void DbModifier::deleteDatabase()
1337 rollback(); // XXX Requires write lock. Also if autoCommit was disabled
1338 // this will incorrectly cause the performDelete to throw CSSMERR_DB_DOES_NOT_EXIST.
1339 StLock
<Mutex
> _(mDbVersionLock
);
1341 // Clean up mModifiedTableMap in case this object gets reused again for
1343 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1344 mModifiedTableMap
.clear();
1347 mAtomicFile
.performDelete();
1351 DbModifier::modifyDatabase()
1358 const uint8
*aFileAddress
;
1360 mVersionId
= mAtomicFile
.enterWrite(aFileAddress
, aLength
, mFileRef
);
1363 // Aquire the mutex protecting mDbVersion
1364 StLock
<Mutex
> _l(mDbVersionLock
);
1365 if (mDbVersion
== nil
|| mDbVersion
->getVersionId() != mVersionId
)
1367 // This will call enterRead(). Now that we hold the write
1368 // lock on the file this ensures we get the same verison
1369 // enterWrite just returned.
1370 mDbVersion
= new DbVersion(mAtomicFile
, mDb
);
1374 // Remove all old modified tables
1375 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1376 mModifiedTableMap
.clear();
1378 // Setup the new tables
1379 DbVersion::TableMap::const_iterator anIt
=
1380 mDbVersion
->mTableMap
.begin();
1381 DbVersion::TableMap::const_iterator anEnd
=
1382 mDbVersion
->mTableMap
.end();
1383 for (; anIt
!= anEnd
; ++anIt
)
1385 auto_ptr
<ModifiedTable
> aTable(new ModifiedTable(anIt
->second
));
1386 mModifiedTableMap
.insert(ModifiedTableMap::value_type(anIt
->first
,
1393 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1394 mModifiedTableMap
.clear();
1401 DbModifier::deleteRecord(Table::Id inTableId
, const RecordId
&inRecordId
)
1404 findTable(inTableId
).deleteRecord(inRecordId
);
1408 DbModifier::insertRecord(Table::Id inTableId
,
1409 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
1410 const CssmData
*inData
)
1413 return findTable(inTableId
).insertRecord(mVersionId
, inAttributes
, inData
);
1417 DbModifier::updateRecord(Table::Id inTableId
, const RecordId
&inRecordId
,
1418 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
1419 const CssmData
*inData
,
1420 CSSM_DB_MODIFY_MODE inModifyMode
)
1422 commit(); // XXX this is not thread safe, but what is?
1424 return findTable(inTableId
).updateRecord(inRecordId
, inAttributes
, inData
, inModifyMode
);
1427 // Create a table associated with a given metarecord, and add the table
1431 DbModifier::createTable(MetaRecord
*inMetaRecord
)
1433 auto_ptr
<MetaRecord
> aMetaRecord(inMetaRecord
);
1434 auto_ptr
<ModifiedTable
> aModifiedTable(new ModifiedTable(inMetaRecord
));
1435 // Now that aModifiedTable is fully constructed it owns inMetaRecord
1436 aMetaRecord
.release();
1438 if (!mModifiedTableMap
.insert
1439 (ModifiedTableMap::value_type(inMetaRecord
->dataRecordType(),
1440 aModifiedTable
.get())).second
)
1442 // XXX Should be CSSMERR_DL_DUPLICATE_RECORDTYPE. Since that
1443 // doesn't exist we report that the metatable's unique index would
1444 // no longer be valid
1445 CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
);
1448 return aModifiedTable
.release();
1452 DbModifier::deleteTable(Table::Id inTableId
)
1455 // Can't delete schema tables.
1456 if (CSSM_DB_RECORDTYPE_SCHEMA_START
<= inTableId
1457 && inTableId
< CSSM_DB_RECORDTYPE_SCHEMA_END
)
1458 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1460 // Find the ModifiedTable and delete it
1461 ModifiedTableMap::iterator it
= mModifiedTableMap
.find(inTableId
);
1462 if (it
== mModifiedTableMap
.end())
1463 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1466 mModifiedTableMap
.erase(it
);
1470 DbModifier::writeAuthSection(uint32 inSectionOffset
)
1472 WriteSection anAuthSection
;
1474 // XXX Put real data into the authsection.
1475 uint32 anOffset
= anAuthSection
.put(0, 0);
1476 anAuthSection
.size(anOffset
);
1478 mAtomicFile
.write(AtomicFile::FromStart
, inSectionOffset
,
1479 anAuthSection
.address(), anAuthSection
.size());
1480 return inSectionOffset
+ anOffset
;
1484 DbModifier::writeSchemaSection(uint32 inSectionOffset
)
1486 uint32 aTableCount
= mModifiedTableMap
.size();
1487 WriteSection
aTableSection(CssmAllocator::standard(),
1488 OffsetTables
+ AtomSize
* aTableCount
);
1489 // Set aTableSection to the correct size.
1490 aTableSection
.size(OffsetTables
+ AtomSize
* aTableCount
);
1491 aTableSection
.put(OffsetTablesCount
, aTableCount
);
1493 uint32 anOffset
= inSectionOffset
+ OffsetTables
+ AtomSize
* aTableCount
;
1494 ModifiedTableMap::const_iterator anIt
= mModifiedTableMap
.begin();
1495 ModifiedTableMap::const_iterator anEnd
= mModifiedTableMap
.end();
1496 for (uint32 aTableNumber
= 0; anIt
!= anEnd
; anIt
++, aTableNumber
++)
1498 // Put the offset to the current table relative to the start of
1499 // this section into the tables array
1500 aTableSection
.put(OffsetTables
+ AtomSize
* aTableNumber
,
1501 anOffset
- inSectionOffset
);
1502 anOffset
= anIt
->second
->writeTable(mAtomicFile
, anOffset
);
1505 aTableSection
.put(OffsetSchemaSize
, anOffset
- inSectionOffset
);
1506 mAtomicFile
.write(AtomicFile::FromStart
, inSectionOffset
,
1507 aTableSection
.address(), aTableSection
.size());
1513 DbModifier::commit()
1519 WriteSection
aHeaderSection(CssmAllocator::standard(), size_t(HeaderSize
));
1520 // Set aHeaderSection to the correct size.
1521 aHeaderSection
.size(HeaderSize
);
1523 // Start writing sections after the header
1524 uint32 anOffset
= HeaderOffset
+ HeaderSize
;
1526 // Write auth section
1527 aHeaderSection
.put(OffsetAuthOffset
, anOffset
);
1528 anOffset
= writeAuthSection(anOffset
);
1529 // Write schema section
1530 aHeaderSection
.put(OffsetSchemaOffset
, anOffset
);
1531 anOffset
= writeSchemaSection(anOffset
);
1533 // Write out the file header.
1534 aHeaderSection
.put(OffsetMagic
, HeaderMagic
);
1535 aHeaderSection
.put(OffsetVersion
, HeaderVersion
);
1536 mAtomicFile
.write(AtomicFile::FromStart
, HeaderOffset
,
1537 aHeaderSection
.address(), aHeaderSection
.size());
1543 rollback(); // Sets mWriting to false;
1550 mAtomicFile
.commit();
1554 DbModifier::rollback()
1559 mAtomicFile
.rollback();
1564 DbModifier::getRecord(Table::Id inTableId
, const RecordId
&inRecordId
,
1565 CSSM_DB_RECORD_ATTRIBUTE_DATA
*inoutAttributes
,
1566 CssmData
*inoutData
, CssmAllocator
&inAllocator
)
1568 // XXX never call commit(), rather search our own record tables.
1569 commit(); // XXX Requires write lock.
1570 return getDbVersion()->getRecord(inTableId
, inRecordId
,
1571 inoutAttributes
, inoutData
, inAllocator
);
1575 DbModifier::createCursor(const CSSM_QUERY
*inQuery
)
1577 // XXX Be smarter as to when we must call commit (i.e. don't
1578 // force commit if the table being queried has not been modified).
1579 commit(); // XXX Requires write lock.
1580 return getDbVersion()->createCursor(inQuery
);
1583 // Insert schema records for a new table into the metatables of the database. This gets
1584 // called while a database is being created.
1587 DbModifier::insertTableSchema(const CssmDbRecordAttributeInfo
&inInfo
,
1588 const CSSM_DB_RECORD_INDEX_INFO
*inIndexInfo
/* = NULL */)
1590 ModifiedTable
&aTable
= findTable(inInfo
.DataRecordType
);
1591 const MetaRecord
&aMetaRecord
= aTable
.getMetaRecord();
1593 CssmAutoDbRecordAttributeData
aRecordBuilder(5); // Set capacity to 5 so we don't need to grow
1595 // Create the entry for the SchemaRelations table.
1596 aRecordBuilder
.add(RelationID
, inInfo
.recordType());
1597 aRecordBuilder
.add(RelationName
, mDb
.recordName(inInfo
.recordType()));
1599 // Insert the record into the SchemaRelations ModifiedTable
1600 findTable(mDb
.schemaRelations
.DataRecordType
).insertRecord(mVersionId
,
1601 &aRecordBuilder
, NULL
);
1603 ModifiedTable
&anAttributeTable
= findTable(mDb
.schemaAttributes
.DataRecordType
);
1604 for (uint32 anIndex
= 0; anIndex
< inInfo
.size(); anIndex
++)
1606 // Create an entry for the SchemaAttributes table.
1607 aRecordBuilder
.clear();
1608 aRecordBuilder
.add(RelationID
, inInfo
.recordType());
1609 aRecordBuilder
.add(AttributeNameFormat
, inInfo
.at(anIndex
).nameFormat());
1611 uint32 attributeId
= aMetaRecord
.metaAttribute(inInfo
.at(anIndex
)).attributeId();
1613 switch (inInfo
.at(anIndex
).nameFormat())
1615 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING
:
1616 aRecordBuilder
.add(AttributeName
, inInfo
.at(anIndex
).Label
.AttributeName
);
1618 case CSSM_DB_ATTRIBUTE_NAME_AS_OID
:
1619 aRecordBuilder
.add(AttributeNameID
, inInfo
.at(anIndex
).Label
.AttributeOID
);
1621 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
:
1624 CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME
);
1627 aRecordBuilder
.add(AttributeID
, attributeId
);
1628 aRecordBuilder
.add(AttributeFormat
, inInfo
.at(anIndex
).format());
1630 // Insert the record into the SchemaAttributes ModifiedTable
1631 anAttributeTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1634 if (inIndexInfo
!= NULL
) {
1636 if (inIndexInfo
->DataRecordType
!= inInfo
.DataRecordType
&&
1637 inIndexInfo
->NumberOfIndexes
> 0)
1638 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1640 ModifiedTable
&indexMetaTable
= findTable(mDb
.schemaIndexes
.DataRecordType
);
1641 uint32 aNumberOfIndexes
= inIndexInfo
->NumberOfIndexes
;
1643 for (uint32 anIndex
= 0; anIndex
< aNumberOfIndexes
; anIndex
++)
1645 const CssmDbIndexInfo
&thisIndex
= CssmDbIndexInfo::overlay(inIndexInfo
->IndexInfo
[anIndex
]);
1647 // make sure the index is supported
1648 if (thisIndex
.dataLocation() != CSSM_DB_INDEX_ON_ATTRIBUTE
)
1649 CssmError::throwMe(CSSMERR_DL_INVALID_INDEX_INFO
);
1651 // assign an index ID: the unique index is ID 0, all others are ID > 0
1653 if (thisIndex
.IndexType
== CSSM_DB_INDEX_UNIQUE
)
1656 indexId
= anIndex
+ 1;
1658 // figure out the attribute ID
1659 uint32 attributeId
=
1660 aMetaRecord
.metaAttribute(thisIndex
.Info
).attributeId();
1662 // Create an entry for the SchemaIndexes table.
1663 aRecordBuilder
.clear();
1664 aRecordBuilder
.add(RelationID
, inInfo
.DataRecordType
);
1665 aRecordBuilder
.add(IndexID
, indexId
);
1666 aRecordBuilder
.add(AttributeID
, attributeId
);
1667 aRecordBuilder
.add(IndexType
, thisIndex
.IndexType
);
1668 aRecordBuilder
.add(IndexedDataLocation
, thisIndex
.IndexedDataLocation
);
1670 // Insert the record into the SchemaIndexes ModifiedTable
1671 indexMetaTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1673 // update the table's index objects
1674 DbMutableIndex
&index
= aTable
.findIndex(indexId
, aMetaRecord
, indexId
== 0);
1675 index
.appendAttribute(attributeId
);
1680 // Insert a new table. The attribute info is required; the index and parsing module
1681 // descriptions are optional. This version gets called during the creation of a
1685 DbModifier::insertTable(const CssmDbRecordAttributeInfo
&inInfo
,
1686 const CSSM_DB_RECORD_INDEX_INFO
*inIndexInfo
/* = NULL */,
1687 const CSSM_DB_PARSING_MODULE_INFO
*inParsingModule
/* = NULL */)
1690 createTable(new MetaRecord(inInfo
));
1691 insertTableSchema(inInfo
, inIndexInfo
);
1694 // Insert a new table. This is the version that gets called when a table is added
1695 // after a database has been created.
1698 DbModifier::insertTable(Table::Id inTableId
, const string
&inTableName
,
1699 uint32 inNumberOfAttributes
,
1700 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
*inAttributeInfo
,
1701 uint32 inNumberOfIndexes
,
1702 const CSSM_DB_SCHEMA_INDEX_INFO
*inIndexInfo
)
1705 ModifiedTable
*aTable
= createTable(new MetaRecord(inTableId
, inNumberOfAttributes
, inAttributeInfo
));
1707 CssmAutoDbRecordAttributeData
aRecordBuilder(6); // Set capacity to 6 so we don't need to grow
1709 // Create the entry for the SchemaRelations table.
1710 aRecordBuilder
.add(RelationID
, inTableId
);
1711 aRecordBuilder
.add(RelationName
, inTableName
);
1713 // Insert the record into the SchemaRelations ModifiedTable
1714 findTable(mDb
.schemaRelations
.DataRecordType
).insertRecord(mVersionId
,
1715 &aRecordBuilder
, NULL
);
1717 ModifiedTable
&anAttributeTable
= findTable(mDb
.schemaAttributes
.DataRecordType
);
1718 for (uint32 anIndex
= 0; anIndex
< inNumberOfAttributes
; anIndex
++)
1720 // Create an entry for the SchemaAttributes table.
1721 aRecordBuilder
.clear();
1722 aRecordBuilder
.add(RelationID
, inTableId
);
1723 // XXX What should this be? We set it to CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER for now
1724 // since the AttributeID is always valid.
1725 aRecordBuilder
.add(AttributeNameFormat
, uint32(CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
));
1726 aRecordBuilder
.add(AttributeID
, inAttributeInfo
[anIndex
].AttributeId
);
1727 if (inAttributeInfo
[anIndex
].AttributeName
)
1728 aRecordBuilder
.add(AttributeName
, inAttributeInfo
[anIndex
].AttributeName
);
1729 if (inAttributeInfo
[anIndex
].AttributeNameID
.Length
> 0)
1730 aRecordBuilder
.add(AttributeNameID
, inAttributeInfo
[anIndex
].AttributeNameID
);
1731 aRecordBuilder
.add(AttributeFormat
, inAttributeInfo
[anIndex
].DataType
);
1733 // Insert the record into the SchemaAttributes ModifiedTable
1734 anAttributeTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1737 ModifiedTable
&anIndexTable
= findTable(mDb
.schemaIndexes
.DataRecordType
);
1738 for (uint32 anIndex
= 0; anIndex
< inNumberOfIndexes
; anIndex
++)
1740 // Create an entry for the SchemaIndexes table.
1741 aRecordBuilder
.clear();
1742 aRecordBuilder
.add(RelationID
, inTableId
);
1743 aRecordBuilder
.add(IndexID
, inIndexInfo
[anIndex
].IndexId
);
1744 aRecordBuilder
.add(AttributeID
, inIndexInfo
[anIndex
].AttributeId
);
1745 aRecordBuilder
.add(IndexType
, inIndexInfo
[anIndex
].IndexType
);
1746 aRecordBuilder
.add(IndexedDataLocation
, inIndexInfo
[anIndex
].IndexedDataLocation
);
1748 // Insert the record into the SchemaIndexes ModifiedTable
1749 anIndexTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1751 // update the table's index objects
1752 DbMutableIndex
&index
= aTable
->findIndex(inIndexInfo
[anIndex
].IndexId
,
1753 aTable
->getMetaRecord(), inIndexInfo
[anIndex
].IndexType
== CSSM_DB_INDEX_UNIQUE
);
1754 index
.appendAttribute(inIndexInfo
[anIndex
].AttributeId
);
1759 DbModifier::findTable(Table::Id inTableId
)
1761 ModifiedTableMap::iterator it
= mModifiedTableMap
.find(inTableId
);
1762 if (it
== mModifiedTableMap
.end())
1763 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1769 // AppleDatabaseManager implementation
1772 AppleDatabaseManager::AppleDatabaseManager(const AppleDatabaseTableName
*tableNames
)
1773 : DatabaseManager(),
1774 mTableNames(tableNames
)
1776 // make sure that a proper set of table ids and names has been provided
1779 CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR
);
1782 for (i
= 0; mTableNames
[i
].mTableName
; i
++) {}
1783 if (i
< AppleDatabaseTableName::kNumRequiredTableNames
)
1784 CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR
);
1789 AppleDatabaseManager::make(const DbName
&inDbName
)
1791 return new AppleDatabase(inDbName
, mTableNames
);
1795 // AppleDbContext implementation
1797 AppleDbContext::AppleDbContext(Database
&inDatabase
,
1798 DatabaseSession
&inDatabaseSession
,
1799 CSSM_DB_ACCESS_TYPE inAccessRequest
,
1800 const AccessCredentials
*inAccessCred
,
1801 const void *inOpenParameters
) :
1802 DbContext(inDatabase
, inDatabaseSession
, inAccessRequest
, inAccessCred
)
1804 const CSSM_APPLEDL_OPEN_PARAMETERS
*anOpenParameters
=
1805 reinterpret_cast<const CSSM_APPLEDL_OPEN_PARAMETERS
*>(inOpenParameters
);
1806 if (anOpenParameters
)
1808 if (anOpenParameters
->length
< sizeof(CSSM_APPLEDL_OPEN_PARAMETERS
)
1809 || anOpenParameters
->version
!= 0)
1810 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS
);
1812 mAutoCommit
= anOpenParameters
->autoCommit
== CSSM_FALSE
? false : true;
1818 AppleDbContext::~AppleDbContext()
1823 // AppleDatabase implementation
1825 AppleDatabase::AppleDatabase(const DbName
&inDbName
, const AppleDatabaseTableName
*tableNames
) :
1827 schemaRelations(tableNames
[AppleDatabaseTableName::kSchemaInfo
].mTableId
,
1828 sizeof(AttrSchemaRelations
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
1829 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaRelations
)),
1830 schemaAttributes(tableNames
[AppleDatabaseTableName::kSchemaAttributes
].mTableId
,
1831 sizeof(AttrSchemaAttributes
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
1832 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaAttributes
)),
1833 schemaIndexes(tableNames
[AppleDatabaseTableName::kSchemaIndexes
].mTableId
,
1834 sizeof(AttrSchemaIndexes
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
1835 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaIndexes
)),
1836 schemaParsingModule(tableNames
[AppleDatabaseTableName::kSchemaParsingModule
].mTableId
,
1837 sizeof(AttrSchemaParsingModule
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
1838 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaParsingModule
)),
1839 mAtomicFile(mDbName
),
1840 mDbModifier(mAtomicFile
, *this),
1841 mTableNames(tableNames
)
1845 AppleDatabase::~AppleDatabase()
1849 // Return the name of a record type. This uses a table that maps record types
1850 // to record names. The table is provided when the database is created.
1852 const char *AppleDatabase::recordName(CSSM_DB_RECORDTYPE inRecordType
) const
1854 if (inRecordType
== CSSM_DL_DB_RECORD_ANY
|| inRecordType
== CSSM_DL_DB_RECORD_ALL_KEYS
)
1855 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1857 for (uint32 i
= 0; mTableNames
[i
].mTableName
; i
++)
1858 if (mTableNames
[i
].mTableId
== inRecordType
)
1859 return mTableNames
[i
].mTableName
;
1865 AppleDatabase::makeDbContext(DatabaseSession
&inDatabaseSession
,
1866 CSSM_DB_ACCESS_TYPE inAccessRequest
,
1867 const AccessCredentials
*inAccessCred
,
1868 const void *inOpenParameters
)
1870 return new AppleDbContext(*this, inDatabaseSession
, inAccessRequest
,
1871 inAccessCred
, inOpenParameters
);
1875 AppleDatabase::dbCreate(DbContext
&inDbContext
, const CSSM_DBINFO
&inDBInfo
,
1876 const CSSM_ACL_ENTRY_INPUT
*inInitialAclEntry
)
1880 StLock
<Mutex
> _(mWriteLock
);
1881 mDbModifier
.createDatabase(inDBInfo
, inInitialAclEntry
);
1885 mDbModifier
.rollback();
1888 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
1889 mDbModifier
.commit();
1893 AppleDatabase::dbOpen(DbContext
&inDbContext
)
1895 mDbModifier
.openDatabase();
1899 AppleDatabase::dbClose()
1901 StLock
<Mutex
> _(mWriteLock
);
1902 mDbModifier
.closeDatabase();
1906 AppleDatabase::dbDelete(DatabaseSession
&inDatabaseSession
,
1907 const AccessCredentials
*inAccessCred
)
1909 StLock
<Mutex
> _(mWriteLock
);
1910 // XXX Check callers credentials.
1911 mDbModifier
.deleteDatabase();
1915 AppleDatabase::createRelation(DbContext
&inDbContext
,
1916 CSSM_DB_RECORDTYPE inRelationID
,
1917 const char *inRelationName
,
1918 uint32 inNumberOfAttributes
,
1919 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
&inAttributeInfo
,
1920 uint32 inNumberOfIndexes
,
1921 const CSSM_DB_SCHEMA_INDEX_INFO
&inIndexInfo
)
1925 StLock
<Mutex
> _(mWriteLock
);
1926 // XXX Fix the refs here.
1927 mDbModifier
.insertTable(inRelationID
, inRelationName
,
1928 inNumberOfAttributes
, &inAttributeInfo
,
1929 inNumberOfIndexes
, &inIndexInfo
);
1933 mDbModifier
.rollback();
1936 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
1937 mDbModifier
.commit();
1941 AppleDatabase::destroyRelation(DbContext
&inDbContext
,
1942 CSSM_DB_RECORDTYPE inRelationID
)
1946 StLock
<Mutex
> _(mWriteLock
);
1947 mDbModifier
.deleteTable(inRelationID
);
1951 mDbModifier
.rollback();
1954 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
1955 mDbModifier
.commit();
1959 AppleDatabase::authenticate(DbContext
&inDbContext
,
1960 CSSM_DB_ACCESS_TYPE inAccessRequest
,
1961 const AccessCredentials
&inAccessCred
)
1963 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
1967 AppleDatabase::getDbAcl(DbContext
&inDbContext
,
1968 const CSSM_STRING
*inSelectionTag
,
1969 uint32
&outNumberOfAclInfos
,
1970 CSSM_ACL_ENTRY_INFO_PTR
&outAclInfos
)
1972 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
1976 AppleDatabase::changeDbAcl(DbContext
&inDbContext
,
1977 const AccessCredentials
&inAccessCred
,
1978 const CSSM_ACL_EDIT
&inAclEdit
)
1980 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
1984 AppleDatabase::getDbOwner(DbContext
&inDbContext
,
1985 CSSM_ACL_OWNER_PROTOTYPE
&outOwner
)
1987 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
1991 AppleDatabase::changeDbOwner(DbContext
&inDbContext
,
1992 const AccessCredentials
&inAccessCred
,
1993 const CSSM_ACL_OWNER_PROTOTYPE
&inNewOwner
)
1995 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
1999 AppleDatabase::getDbNameFromHandle(const DbContext
&inDbContext
) const
2001 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2004 CSSM_DB_UNIQUE_RECORD_PTR
2005 AppleDatabase::dataInsert(DbContext
&inDbContext
,
2006 CSSM_DB_RECORDTYPE inRecordType
,
2007 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
2008 const CssmData
*inData
)
2010 CSSM_DB_UNIQUE_RECORD_PTR anUniqueRecordPtr
= NULL
;
2013 StLock
<Mutex
> _(mWriteLock
);
2014 const RecordId aRecordId
=
2015 mDbModifier
.insertRecord(inRecordType
, inAttributes
, inData
);
2017 anUniqueRecordPtr
= createUniqueRecord(inDbContext
, inRecordType
,
2019 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2020 mDbModifier
.commit();
2024 if (anUniqueRecordPtr
!= NULL
)
2025 freeUniqueRecord(inDbContext
, *anUniqueRecordPtr
);
2027 mDbModifier
.rollback();
2031 return anUniqueRecordPtr
;
2035 AppleDatabase::dataDelete(DbContext
&inDbContext
,
2036 const CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
)
2040 StLock
<Mutex
> _(mWriteLock
);
2042 const RecordId
aRecordId(parseUniqueRecord(inUniqueRecord
, aTableId
));
2043 mDbModifier
.deleteRecord(aTableId
, aRecordId
);
2047 mDbModifier
.rollback();
2051 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2052 mDbModifier
.commit();
2056 AppleDatabase::dataModify(DbContext
&inDbContext
,
2057 CSSM_DB_RECORDTYPE inRecordType
,
2058 CSSM_DB_UNIQUE_RECORD
&inoutUniqueRecord
,
2059 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributesToBeModified
,
2060 const CssmData
*inDataToBeModified
,
2061 CSSM_DB_MODIFY_MODE inModifyMode
)
2065 StLock
<Mutex
> _(mWriteLock
);
2067 const RecordId aRecordId
=
2068 mDbModifier
.updateRecord(aTableId
,
2069 parseUniqueRecord(inoutUniqueRecord
, aTableId
),
2070 inAttributesToBeModified
,
2073 updateUniqueRecord(inDbContext
, inRecordType
, aRecordId
, inoutUniqueRecord
);
2077 mDbModifier
.rollback();
2081 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2082 mDbModifier
.commit();
2086 AppleDatabase::dataGetFirst(DbContext
&inDbContext
,
2087 const DLQuery
*inQuery
,
2088 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
2089 CssmData
*inoutData
,
2090 CSSM_DB_UNIQUE_RECORD_PTR
&outUniqueRecord
)
2092 // XXX: register Cursor with DbContext and have DbContext call
2093 // dataAbortQuery for all outstanding Query objects on close.
2094 auto_ptr
<Cursor
> aCursor(mDbModifier
.createCursor(inQuery
));
2098 if (!aCursor
->next(aTableId
, inoutAttributes
, inoutData
,
2099 inDbContext
.mDatabaseSession
, aRecordId
))
2100 // return a NULL handle, and implicitly delete the cursor
2101 return CSSM_INVALID_HANDLE
;
2103 outUniqueRecord
= createUniqueRecord(inDbContext
, aTableId
, aRecordId
);
2104 return aCursor
.release()->handle(); // We didn't throw so keep the Cursor around.
2108 AppleDatabase::dataGetNext(DbContext
&inDbContext
,
2109 CSSM_HANDLE inResultsHandle
,
2110 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
2111 CssmData
*inoutData
,
2112 CSSM_DB_UNIQUE_RECORD_PTR
&outUniqueRecord
)
2114 auto_ptr
<Cursor
> aCursor(&findHandle
<Cursor
>(inResultsHandle
, CSSMERR_DL_INVALID_RESULTS_HANDLE
));
2118 if (!aCursor
->next(aTableId
, inoutAttributes
, inoutData
, inDbContext
.mDatabaseSession
, aRecordId
))
2121 outUniqueRecord
= createUniqueRecord(inDbContext
, aTableId
, aRecordId
);
2128 AppleDatabase::dataAbortQuery(DbContext
&inDbContext
,
2129 CSSM_HANDLE inResultsHandle
)
2131 delete &findHandle
<Cursor
>(inResultsHandle
, CSSMERR_DL_INVALID_RESULTS_HANDLE
);
2135 AppleDatabase::dataGetFromUniqueRecordId(DbContext
&inDbContext
,
2136 const CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
,
2137 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
2138 CssmData
*inoutData
)
2141 const RecordId
aRecordId(parseUniqueRecord(inUniqueRecord
, aTableId
));
2142 // XXX Change CDSA spec to use new RecordId returned by this function
2143 mDbModifier
.getRecord(aTableId
, aRecordId
, inoutAttributes
, inoutData
,
2144 inDbContext
.mDatabaseSession
);
2148 AppleDatabase::freeUniqueRecord(DbContext
&inDbContext
,
2149 CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
)
2151 if (inUniqueRecord
.RecordIdentifier
.Length
!= 0
2152 && inUniqueRecord
.RecordIdentifier
.Data
!= NULL
)
2154 inUniqueRecord
.RecordIdentifier
.Length
= 0;
2155 inDbContext
.mDatabaseSession
.free(inUniqueRecord
.RecordIdentifier
.Data
);
2157 inDbContext
.mDatabaseSession
.free(&inUniqueRecord
);
2161 AppleDatabase::updateUniqueRecord(DbContext
&inDbContext
,
2162 CSSM_DB_RECORDTYPE inTableId
,
2163 const RecordId
&inRecordId
,
2164 CSSM_DB_UNIQUE_RECORD
&inoutUniqueRecord
)
2166 uint32
*aBuffer
= reinterpret_cast<uint32
*>(inoutUniqueRecord
.RecordIdentifier
.Data
);
2167 aBuffer
[0] = inTableId
;
2168 aBuffer
[1] = inRecordId
.mRecordNumber
;
2169 aBuffer
[2] = inRecordId
.mCreateVersion
;
2170 aBuffer
[3] = inRecordId
.mRecordVersion
;
2173 CSSM_DB_UNIQUE_RECORD_PTR
2174 AppleDatabase::createUniqueRecord(DbContext
&inDbContext
,
2175 CSSM_DB_RECORDTYPE inTableId
,
2176 const RecordId
&inRecordId
)
2178 CSSM_DB_UNIQUE_RECORD_PTR aUniqueRecord
=
2179 inDbContext
.mDatabaseSession
.alloc
<CSSM_DB_UNIQUE_RECORD
>();
2180 memset(aUniqueRecord
, 0, sizeof(*aUniqueRecord
));
2181 aUniqueRecord
->RecordIdentifier
.Length
= sizeof(uint32
) * 4;
2184 aUniqueRecord
->RecordIdentifier
.Data
=
2185 inDbContext
.mDatabaseSession
.alloc
<uint8
>(sizeof(uint32
) * 4);
2186 updateUniqueRecord(inDbContext
, inTableId
, inRecordId
, *aUniqueRecord
);
2190 inDbContext
.mDatabaseSession
.free(aUniqueRecord
);
2194 return aUniqueRecord
;
2198 AppleDatabase::parseUniqueRecord(const CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
,
2199 CSSM_DB_RECORDTYPE
&outTableId
)
2201 if (inUniqueRecord
.RecordIdentifier
.Length
!= sizeof(uint32
) * 4)
2202 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID
);
2204 uint32
*aBuffer
= reinterpret_cast<uint32
*>(inUniqueRecord
.RecordIdentifier
.Data
);
2205 outTableId
= aBuffer
[0];
2206 return RecordId(aBuffer
[1], aBuffer
[2], aBuffer
[3]);
2210 AppleDatabase::passThrough(DbContext
&dbContext
,
2211 uint32 passThroughId
,
2212 const void *inputParams
,
2213 void **outputParams
)
2215 switch (passThroughId
)
2217 case CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
:
2219 CSSM_BOOL on
= reinterpret_cast<CSSM_BOOL
>(inputParams
);
2220 safer_cast
<AppleDbContext
&>(dbContext
).autoCommit(on
);
2224 case CSSM_APPLEFILEDL_COMMIT
:
2225 mDbModifier
.commit();
2228 case CSSM_APPLEFILEDL_ROLLBACK
:
2229 mDbModifier
.rollback();
2233 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);