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>
33 Table::Table(const ReadSection
&inTableSection
) :
34 mMetaRecord(inTableSection
[OffsetId
]),
35 mTableSection(inTableSection
),
36 mRecordsCount(inTableSection
[OffsetRecordsCount
]),
37 mFreeListHead(inTableSection
[OffsetFreeListHead
]),
38 mRecordNumbersCount(inTableSection
[OffsetRecordNumbersCount
])
40 // can't easily initialize indexes here, since meta record is incomplete
41 // until much later... see DbVersion::open()
46 for_each_map_delete(mIndexMap
.begin(), mIndexMap
.end());
50 Table::readIndexSection()
52 uint32 indexSectionOffset
= mTableSection
.at(OffsetIndexesOffset
);
54 uint32 numIndexes
= mTableSection
.at(indexSectionOffset
+ AtomSize
);
56 for (uint32 i
= 0; i
< numIndexes
; i
++) {
57 uint32 indexOffset
= mTableSection
.at(indexSectionOffset
+ (i
+ 2) * AtomSize
);
58 ReadSection
indexSection(mTableSection
.subsection(indexOffset
));
60 auto_ptr
<DbConstIndex
> index(new DbConstIndex(*this, indexSection
));
61 mIndexMap
.insert(ConstIndexMap::value_type(index
->indexId(), index
.get()));
67 Table::createCursor(const CSSM_QUERY
*inQuery
, const DbVersion
&inDbVersion
) const
69 // if an index matches the query, return a cursor which uses the index
71 ConstIndexMap::const_iterator it
;
74 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
75 if (it
->second
->matchesQuery(*inQuery
, queryKey
)) {
76 IndexCursor
*cursor
= new IndexCursor(queryKey
, inDbVersion
, *this, it
->second
);
80 // otherwise, return a cursor that iterates over all table records
82 return new LinearCursor(inQuery
, inDbVersion
, *this);
86 Table::getRecordSection(uint32 inRecordNumber
) const
88 if (inRecordNumber
>= mRecordNumbersCount
)
89 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID
);
91 uint32 aRecordOffset
= mTableSection
[OffsetRecordNumbers
+ AtomSize
94 // Check if this RecordNumber has been deleted.
95 if (aRecordOffset
& 1 || aRecordOffset
== 0)
96 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
98 return MetaRecord::readSection(mTableSection
, aRecordOffset
);
102 Table::getRecord(const RecordId
&inRecordId
,
103 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
105 CssmAllocator
&inAllocator
) const
107 const ReadSection aRecordSection
= getRecordSection(inRecordId
.mRecordNumber
);
108 const RecordId aRecordId
= MetaRecord::unpackRecordId(aRecordSection
);
110 // Make sure the RecordNumber matches that in the RecordId we just retrived.
111 if (aRecordId
.mRecordNumber
!= inRecordId
.mRecordNumber
)
112 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
114 if (aRecordId
.mCreateVersion
!= inRecordId
.mCreateVersion
)
115 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID
);
117 // XXX Figure out which value to pass for inQueryFlags (5th) argument
118 mMetaRecord
.unpackRecord(aRecordSection
, inAllocator
, inoutAttributes
,
124 Table::popFreeList(uint32
&aFreeListHead
) const
126 assert(aFreeListHead
| 1);
127 uint32 anOffset
= aFreeListHead
^ 1;
128 uint32 aRecordNumber
= (anOffset
- OffsetRecordNumbers
) / AtomSize
;
129 aFreeListHead
= mTableSection
[anOffset
];
130 return aRecordNumber
;
134 Table::getRecordsSection() const
136 return mTableSection
.subsection(mTableSection
[OffsetRecords
]);
140 Table::matchesTableId(Id inTableId
) const
142 Id anId
= mMetaRecord
.dataRecordType();
143 if (inTableId
== CSSM_DL_DB_RECORD_ANY
) // All non schema tables.
144 return !(CSSM_DB_RECORDTYPE_SCHEMA_START
<= anId
145 && anId
< CSSM_DB_RECORDTYPE_SCHEMA_END
);
147 if (inTableId
== CSSM_DL_DB_RECORD_ALL_KEYS
) // All key tables.
148 return (anId
== CSSM_DL_DB_RECORD_PUBLIC_KEY
149 || anId
== CSSM_DL_DB_RECORD_PRIVATE_KEY
150 || anId
== CSSM_DL_DB_RECORD_SYMMETRIC_KEY
);
152 return inTableId
== anId
; // Only if exact match.
159 ModifiedTable::ModifiedTable(const Table
*inTable
) :
162 mRecordNumberCount(inTable
->recordNumberCount()),
163 mFreeListHead(inTable
->freeListHead()),
168 ModifiedTable::ModifiedTable(MetaRecord
*inMetaRecord
) :
170 mNewMetaRecord(inMetaRecord
),
171 mRecordNumberCount(0),
177 ModifiedTable::~ModifiedTable()
179 for_each_map_delete(mIndexMap
.begin(), mIndexMap
.end());
180 for_each_map_delete(mInsertedMap
.begin(), mInsertedMap
.end());
182 delete mNewMetaRecord
;
186 ModifiedTable::deleteRecord(const RecordId
&inRecordId
)
190 uint32 aRecordNumber
= inRecordId
.mRecordNumber
;
192 // remove the record from all the indexes
193 MutableIndexMap::iterator it
;
194 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
195 it
->second
->removeRecord(aRecordNumber
);
197 InsertedMap::iterator anIt
= mInsertedMap
.find(inRecordId
.mRecordNumber
);
198 if (anIt
== mInsertedMap
.end())
200 // If we have no old table than this record can not exist yet.
202 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
204 const RecordId aRecordId
= MetaRecord::unpackRecordId(mTable
->getRecordSection(aRecordNumber
));
205 if (aRecordId
.mRecordVersion
!= inRecordId
.mRecordVersion
)
206 CssmError::throwMe(CSSMERR_DL_RECORD_MODIFIED
);
208 // Schedule the record for deletion
209 if (!mDeletedSet
.insert(aRecordNumber
).second
)
210 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // It was already deleted
214 const RecordId aRecordId
= MetaRecord::unpackRecordId(*anIt
->second
);
215 if (aRecordId
.mCreateVersion
!= inRecordId
.mCreateVersion
)
216 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
218 if (aRecordId
.mRecordVersion
!= inRecordId
.mRecordVersion
)
219 CssmError::throwMe(CSSMERR_DL_RECORD_MODIFIED
);
221 // Remove the inserted (but uncommited) record. It should already be in mDeletedSet
222 // if it existed previously in mTable.
223 mInsertedMap
.erase(anIt
);
229 ModifiedTable::insertRecord(AtomicFile::VersionId inVersionId
,
230 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
231 const CssmData
*inData
)
235 auto_ptr
<WriteSection
> aWriteSection(new WriteSection());
236 getMetaRecord().packRecord(*aWriteSection
, inAttributes
, inData
);
237 uint32 aRecordNumber
= nextRecordNumber();
239 // add the record to all the indexes; this will throw if the new record
240 // violates a unique index
241 MutableIndexMap::iterator it
;
242 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
243 it
->second
->insertRecord(aRecordNumber
, *(aWriteSection
.get()));
245 // schedule the record for insertion
246 RecordId
aRecordId(aRecordNumber
, inVersionId
);
247 MetaRecord::packRecordId(aRecordId
, *aWriteSection
);
248 mInsertedMap
.insert(InsertedMap::value_type(aRecordNumber
, aWriteSection
.get()));
250 aWriteSection
.release();
256 ModifiedTable::updateRecord(const RecordId
&inRecordId
,
257 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
258 const CssmData
*inData
,
259 CSSM_DB_MODIFY_MODE inModifyMode
)
263 uint32 aRecordNumber
= inRecordId
.mRecordNumber
;
264 InsertedMap::iterator anIt
= mInsertedMap
.find(inRecordId
.mRecordNumber
);
266 // aReUpdate is true iff we are updating an already updated record.
267 bool aReUpdate
= anIt
!= mInsertedMap
.end();
269 // If we are not re-updating and there is no old table than this record does not exist yet.
270 if (!aReUpdate
&& !mTable
)
271 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
273 const ReadSection
&anOldDbRecord
= aReUpdate
? *anIt
->second
: mTable
->getRecordSection(aRecordNumber
);
274 const RecordId aRecordId
= MetaRecord::unpackRecordId(anOldDbRecord
);
276 // Did someone else delete the record we are trying to update.
277 if (aRecordId
.mCreateVersion
!= inRecordId
.mCreateVersion
)
278 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
280 // Is the record we that our update is based on current?
281 if (aRecordId
.mRecordVersion
!= inRecordId
.mRecordVersion
)
282 CssmError::throwMe(CSSMERR_DL_STALE_UNIQUE_RECORD
);
284 // Update the actual packed record.
285 auto_ptr
<WriteSection
> aDbRecord(new WriteSection());
286 getMetaRecord().updateRecord(anOldDbRecord
, *aDbRecord
,
287 CssmDbRecordAttributeData::overlay(inAttributes
), inData
, inModifyMode
);
290 // Bump the RecordVersion of this record.
291 RecordId
aNewRecordId(aRecordNumber
, inRecordId
.mCreateVersion
, inRecordId
.mRecordVersion
+ 1);
292 // Store the RecordVersion in the packed aDbRecord.
293 MetaRecord::packRecordId(aNewRecordId
, *aDbRecord
);
295 if (!aReUpdate
&& !mDeletedSet
.insert(aRecordNumber
).second
)
296 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // Record was already in mDeletedSet
300 // remove the original record from all the indexes
301 MutableIndexMap::iterator it
;
302 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
303 it
->second
->removeRecord(aRecordNumber
);
305 // add the updated record to all the indexes; this will throw if the new record
306 // violates a unique index
307 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
308 it
->second
->insertRecord(aRecordNumber
, *(aDbRecord
.get()));
310 mInsertedMap
.insert(InsertedMap::value_type(aRecordNumber
, aDbRecord
.get()));
316 mDeletedSet
.erase(aRecordNumber
);
324 ModifiedTable::nextRecordNumber()
326 // If we still have unused free records in mTable get the next one.
328 return mTable
->popFreeList(mFreeListHead
);
330 // Bump up the mRecordNumberCount so we don't reuse the same one.
331 return mRecordNumberCount
++;
335 ModifiedTable::recordNumberCount() const
337 uint32 anOldMax
= !mTable
? 0 : mTable
->recordNumberCount() - 1;
338 uint32 anInsertedMax
= mInsertedMap
.empty() ? 0 : mInsertedMap
.rbegin()->first
;
340 DeletedSet::reverse_iterator anIt
= mDeletedSet
.rbegin();
341 DeletedSet::reverse_iterator anEnd
= mDeletedSet
.rend();
342 for (; anIt
!= anEnd
; anIt
++)
344 if (*anIt
!= anOldMax
|| anOldMax
<= anInsertedMax
)
349 return max(anOldMax
,anInsertedMax
) + 1;
353 ModifiedTable::getMetaRecord() const
355 return mNewMetaRecord
? *mNewMetaRecord
: mTable
->getMetaRecord();
358 // prepare to modify the table
361 ModifiedTable::modifyTable()
364 createMutableIndexes();
369 // create mutable indexes from the read-only indexes in the underlying table
372 ModifiedTable::createMutableIndexes()
377 Table::ConstIndexMap::const_iterator it
;
378 for (it
= mTable
->mIndexMap
.begin(); it
!= mTable
->mIndexMap
.end(); it
++) {
379 auto_ptr
<DbMutableIndex
> mutableIndex(new DbMutableIndex(*it
->second
));
380 mIndexMap
.insert(MutableIndexMap::value_type(it
->first
, mutableIndex
.get()));
381 mutableIndex
.release();
385 // find, and create if needed, an index with the given id
388 ModifiedTable::findIndex(uint32 indexId
, const MetaRecord
&metaRecord
, bool isUniqueIndex
)
390 MutableIndexMap::iterator it
= mIndexMap
.find(indexId
);
392 if (it
== mIndexMap
.end()) {
393 // create the new index
394 auto_ptr
<DbMutableIndex
> index(new DbMutableIndex(metaRecord
, indexId
, isUniqueIndex
));
395 it
= mIndexMap
.insert(MutableIndexMap::value_type(indexId
, index
.get())).first
;
403 ModifiedTable::writeIndexSection(WriteSection
&tableSection
, uint32 offset
)
405 MutableIndexMap::iterator it
;
407 tableSection
.put(Table::OffsetIndexesOffset
, offset
);
409 // leave room for the size, to be written later
410 uint32 indexSectionOffset
= offset
;
413 offset
= tableSection
.put(offset
, mIndexMap
.size());
415 // leave room for the array of offsets to the indexes
416 uint32 indexOffsetOffset
= offset
;
417 offset
+= mIndexMap
.size() * AtomSize
;
420 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++) {
421 indexOffsetOffset
= tableSection
.put(indexOffsetOffset
, offset
);
422 offset
= it
->second
->writeIndex(tableSection
, offset
);
425 // write the total index section size
426 tableSection
.put(indexSectionOffset
, offset
- indexSectionOffset
);
432 ModifiedTable::writeTable(AtomicFile
&inAtomicFile
, uint32 inSectionOffset
)
434 if (mTable
&& !mIsModified
) {
435 // the table has not been modified, so we can just dump the old table
436 // section into the new database
438 const ReadSection
&tableSection
= mTable
->getTableSection();
439 uint32 tableSize
= tableSection
.at(Table::OffsetSize
);
441 inAtomicFile
.write(AtomicFile::FromStart
, inSectionOffset
,
442 tableSection
.range(Range(0, tableSize
)), tableSize
);
444 return inSectionOffset
+ tableSize
;
447 // We should have an old mTable or a mNewMetaRecord but not both.
448 assert(mTable
!= nil
^ mNewMetaRecord
!= nil
);
449 const MetaRecord
&aNewMetaRecord
= getMetaRecord();
451 uint32 aRecordsCount
= 0;
452 uint32 aRecordNumbersCount
= recordNumberCount();
453 uint32 aRecordsOffset
= Table::OffsetRecordNumbers
+ AtomSize
* aRecordNumbersCount
;
454 WriteSection
aTableSection(CssmAllocator::standard(), aRecordsOffset
);
455 aTableSection
.size(aRecordsOffset
);
456 aTableSection
.put(Table::OffsetId
, aNewMetaRecord
.dataRecordType());
457 aTableSection
.put(Table::OffsetRecords
, aRecordsOffset
);
458 aTableSection
.put(Table::OffsetRecordNumbersCount
, aRecordNumbersCount
);
460 uint32 anOffset
= inSectionOffset
+ aRecordsOffset
;
464 // XXX Handle schema changes in the future.
465 assert(mNewMetaRecord
== nil
);
467 // We have a modified old table so copy all non deleted records
468 // The code below is rather elaborate, but this is because it attempts
469 // to copy large ranges of non deleted records with single calls
470 // to AtomicFile::write()
471 uint32 anOldRecordsCount
= mTable
->getRecordsCount();
472 ReadSection aRecordsSection
= mTable
->getRecordsSection();
473 uint32 aReadOffset
= 0; // Offset of current record
474 uint32 aWriteOffset
= aRecordsOffset
; // Offset for current write record
475 uint32 aBlockStart
= aReadOffset
; // Starting point for read
476 uint32 aBlockSize
= 0; // Size of block to read
477 for (uint32 aRecord
= 0; aRecord
< anOldRecordsCount
; aRecord
++)
479 ReadSection aRecordSection
= MetaRecord::readSection(aRecordsSection
, aReadOffset
);
480 uint32 aRecordNumber
= MetaRecord::unpackRecordNumber(aRecordSection
);
481 uint32 aRecordSize
= aRecordSection
.size();
482 aReadOffset
+= aRecordSize
;
483 if (mDeletedSet
.find(aRecordNumber
) == mDeletedSet
.end())
485 // This record has not been deleted. Register the offset
486 // at which it will be in the new file in aTableSection.
487 aTableSection
.put(Table::OffsetRecordNumbers
488 + AtomSize
* aRecordNumber
,
490 aWriteOffset
+= aRecordSize
;
491 aBlockSize
+= aRecordSize
;
493 // XXX update all indexes being created.
497 // The current record has been deleted. Copy all records up
498 // to but not including the current one to the new file.
501 inAtomicFile
.write(AtomicFile::FromStart
, anOffset
,
502 aRecordsSection
.range(Range(aBlockStart
,
505 anOffset
+= aBlockSize
;
508 // Set the start of the next block to the start of the next
509 // record, and the size of the block to 0.
510 aBlockStart
= aReadOffset
;
512 } // if (mDeletedSet..)
513 } // for (aRecord...)
515 // Copy all records that have not yet been copied to the new file.
518 inAtomicFile
.write(AtomicFile::FromStart
, anOffset
,
519 aRecordsSection
.range(Range(aBlockStart
,
522 anOffset
+= aBlockSize
;
526 // Now add all inserted records to the table.
527 InsertedMap::const_iterator anIt
= mInsertedMap
.begin();
528 InsertedMap::const_iterator anEnd
= mInsertedMap
.end();
529 // Iterate over all inserted objects.
530 for (; anIt
!= anEnd
; anIt
++)
532 // Write out each inserted/modified record
533 const WriteSection
&aRecord
= *anIt
->second
;
534 uint32 aRecordNumber
= anIt
->first
;
535 // Put offset relative to start of this table in recordNumber array.
536 aTableSection
.put(Table::OffsetRecordNumbers
+ AtomSize
* aRecordNumber
,
537 anOffset
- inSectionOffset
);
538 inAtomicFile
.write(AtomicFile::FromStart
, anOffset
,
539 aRecord
.address(), aRecord
.size());
540 anOffset
+= aRecord
.size();
542 // XXX update all indexes being created.
545 // Reconstruct the freelist (this is O(N) where N is the number of recordNumbers)
546 // We could implement it faster by using the old freelist and skipping the records
547 // that have been inserted. However building the freelist for the newly used
548 // recordNumbers (not in mTable) would look like the code below anyway (starting
549 // from mTable->recordNumberCount()).
550 // The first part of this would be O(M Log(N)) (where M is the old number of
551 // free records, and N is the number of newly inserted records)
552 // The second part would be O(N) where N is the currently max RecordNumber
553 // in use - the old max RecordNumber in use.
554 uint32 aFreeListHead
= 0; // Link to previous free record
555 for (uint32 aRecordNumber
= 0; aRecordNumber
< aRecordNumbersCount
; aRecordNumber
++)
557 // Make the freelist a list of all records with 0 offset (non existing).
558 if (!aTableSection
.at(Table::OffsetRecordNumbers
+ AtomSize
* aRecordNumber
))
560 aTableSection
.put(Table::OffsetRecordNumbers
561 + AtomSize
* aRecordNumber
,
563 // Make aFreeListHead point to the previous free recordNumber slot in the table.
564 aFreeListHead
= (Table::OffsetRecordNumbers
+ AtomSize
* aRecordNumber
) | 1;
567 aTableSection
.put(Table::OffsetFreeListHead
, aFreeListHead
);
569 anOffset
-= inSectionOffset
;
571 // Write out indexes, which are part of the table section
574 uint32 indexOffset
= anOffset
;
575 anOffset
= writeIndexSection(aTableSection
, anOffset
);
576 inAtomicFile
.write(AtomicFile::FromStart
, inSectionOffset
+ indexOffset
,
577 aTableSection
.address() + indexOffset
, anOffset
- indexOffset
);
580 // Set the section size and recordCount.
581 aTableSection
.put(Table::OffsetSize
, anOffset
);
582 aTableSection
.put(Table::OffsetRecordsCount
, aRecordsCount
);
584 // Write out aTableSection header.
585 inAtomicFile
.write(AtomicFile::FromStart
, inSectionOffset
,
586 aTableSection
.address(), aTableSection
.size());
588 return anOffset
+ inSectionOffset
;
597 // Attribute definitions
599 static const CSSM_DB_ATTRIBUTE_INFO RelationID
=
601 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
603 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
605 static const CSSM_DB_ATTRIBUTE_INFO RelationName
=
607 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
609 CSSM_DB_ATTRIBUTE_FORMAT_STRING
611 static const CSSM_DB_ATTRIBUTE_INFO AttributeID
=
613 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
615 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
617 static const CSSM_DB_ATTRIBUTE_INFO AttributeNameFormat
=
619 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
620 {"AttributeNameFormat"},
621 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
623 static const CSSM_DB_ATTRIBUTE_INFO AttributeName
=
625 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
627 CSSM_DB_ATTRIBUTE_FORMAT_STRING
629 static const CSSM_DB_ATTRIBUTE_INFO AttributeNameID
=
631 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
633 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
635 static const CSSM_DB_ATTRIBUTE_INFO AttributeFormat
=
637 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
639 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
641 static const CSSM_DB_ATTRIBUTE_INFO IndexID
=
643 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
645 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
647 static const CSSM_DB_ATTRIBUTE_INFO IndexType
=
649 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
651 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
653 static const CSSM_DB_ATTRIBUTE_INFO IndexedDataLocation
=
655 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
656 {"IndexedDataLocation"},
657 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
659 static const CSSM_DB_ATTRIBUTE_INFO ModuleID
=
661 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
663 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
665 static const CSSM_DB_ATTRIBUTE_INFO AddinVersion
=
667 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
669 CSSM_DB_ATTRIBUTE_FORMAT_STRING
671 static const CSSM_DB_ATTRIBUTE_INFO SSID
=
673 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
675 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
677 static const CSSM_DB_ATTRIBUTE_INFO SubserviceType
=
679 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
681 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
684 #define ATTRIBUTE(type, name) \
685 { CSSM_DB_ATTRIBUTE_NAME_AS_STRING, { #name }, CSSM_DB_ATTRIBUTE_FORMAT_ ## type }
687 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaRelations
[] =
689 //RelationID, RelationName
690 ATTRIBUTE(UINT32
, RelationID
),
691 ATTRIBUTE(STRING
, RelationName
)
694 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaAttributes
[] =
696 //RelationID, AttributeID,
697 //AttributeNameFormat, AttributeName, AttributeNameID,
699 ATTRIBUTE(UINT32
, RelationID
),
700 ATTRIBUTE(UINT32
, AttributeID
),
701 ATTRIBUTE(UINT32
, AttributeNameFormat
),
702 ATTRIBUTE(STRING
, AttributeName
),
703 ATTRIBUTE(BLOB
, AttributeNameID
),
704 ATTRIBUTE(UINT32
, AttributeFormat
)
707 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaIndexes
[] =
709 ATTRIBUTE(UINT32
, RelationID
),
710 ATTRIBUTE(UINT32
, IndexID
),
711 ATTRIBUTE(UINT32
, AttributeID
),
712 ATTRIBUTE(UINT32
, IndexType
),
713 ATTRIBUTE(UINT32
, IndexedDataLocation
)
714 //RelationID, IndexID, AttributeID,
715 //IndexType, IndexedDataLocation
718 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaParsingModule
[] =
720 ATTRIBUTE(UINT32
, RelationID
),
721 ATTRIBUTE(UINT32
, AttributeID
),
722 ATTRIBUTE(BLOB
, ModuleID
),
723 ATTRIBUTE(STRING
, AddinVersion
),
724 ATTRIBUTE(UINT32
, SSID
),
725 ATTRIBUTE(UINT32
, SubserviceType
)
726 //RelationID, AttributeID,
727 //ModuleID, AddinVersion, SSID, SubserviceType
735 DbVersion::DbVersion(AtomicFile
&inDatabaseFile
,
736 const AppleDatabase
&db
) :
737 mDatabase(reinterpret_cast<const uint8
*>(NULL
), 0), mDatabaseFile(&inDatabaseFile
),
740 const uint8
*aFileAddress
;
742 mVersionId
= mDatabaseFile
->enterRead(aFileAddress
, aLength
);
743 mDatabase
= ReadSection(aFileAddress
, aLength
);
747 DbVersion::~DbVersion()
751 for_each_map_delete(mTableMap
.begin(), mTableMap
.end());
753 mDatabaseFile
->exitRead(mVersionId
);
759 DbVersion::isDirty() const
762 return mDatabaseFile
->isDirty(mVersionId
);
772 // This is the oposite of DbModifier::commit()
773 const ReadSection aHeaderSection
= mDatabase
.subsection(HeaderOffset
,
775 if (aHeaderSection
.at(OffsetMagic
) != HeaderMagic
)
776 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
778 // We currently only support one version. If we support additional
779 // file format versions in the future fix this.
780 uint32 aVersion
= aHeaderSection
.at(OffsetVersion
);
781 if (aVersion
!= HeaderVersion
)
782 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
784 //const ReadSection anAuthSection =
785 // mDatabase.subsection(HeaderOffset + aHeaderSection.at(OffsetAuthOffset));
786 // XXX Do something with anAuthSection.
788 uint32 aSchemaOffset
= aHeaderSection
.at(OffsetSchemaOffset
);
789 const ReadSection aSchemaSection
=
790 mDatabase
.subsection(HeaderOffset
+ aSchemaOffset
);
792 uint32 aSchemaSize
= aSchemaSection
[OffsetSchemaSize
];
793 // Make sure that the given range exists.
794 aSchemaSection
.subsection(0, aSchemaSize
);
795 uint32 aTableCount
= aSchemaSection
[OffsetTablesCount
];
797 // Assert that the size of this section is big enough.
798 if (aSchemaSize
< OffsetTables
+ AtomSize
* aTableCount
)
799 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
801 for (uint32 aTableNumber
= 0; aTableNumber
< aTableCount
;
804 uint32 aTableOffset
= aSchemaSection
.at(OffsetTables
+ AtomSize
806 // XXX Set the size boundary on aTableSection.
807 const ReadSection aTableSection
=
808 aSchemaSection
.subsection(aTableOffset
);
809 auto_ptr
<Table
> aTable(new Table(aTableSection
));
810 Table::Id aTableId
= aTable
->getMetaRecord().dataRecordType();
811 mTableMap
.insert(TableMap::value_type(aTableId
, aTable
.get()));
815 // Fill in the schema for the meta tables.
817 findTable(mDb
.schemaRelations
.DataRecordType
).getMetaRecord().
818 setRecordAttributeInfo(mDb
.schemaRelations
);
819 findTable(mDb
.schemaIndexes
.DataRecordType
).getMetaRecord().
820 setRecordAttributeInfo(mDb
.schemaIndexes
);
821 findTable(mDb
.schemaParsingModule
.DataRecordType
).getMetaRecord().
822 setRecordAttributeInfo(mDb
.schemaParsingModule
);
824 // OK, we have created all the tables in the tableMap. Now
825 // lets read the schema and proccess it accordingly.
826 // Iterate over all schema records.
827 Table
&aTable
= findTable(mDb
.schemaAttributes
.DataRecordType
);
828 aTable
.getMetaRecord().setRecordAttributeInfo(mDb
.schemaAttributes
);
829 uint32 aRecordsCount
= aTable
.getRecordsCount();
830 ReadSection aRecordsSection
= aTable
.getRecordsSection();
831 uint32 aReadOffset
= 0;
832 const MetaRecord
&aMetaRecord
= aTable
.getMetaRecord();
834 CSSM_DB_ATTRIBUTE_DATA aRelationIDData
=
840 CSSM_DB_ATTRIBUTE_DATA aAttributeIDData
=
846 CSSM_DB_ATTRIBUTE_DATA aAttributeNameFormatData
=
852 CSSM_DB_ATTRIBUTE_DATA aAttributeNameData
=
858 CSSM_DB_ATTRIBUTE_DATA aAttributeNameIDData
=
864 CSSM_DB_ATTRIBUTE_DATA aAttributeFormatData
=
870 CSSM_DB_ATTRIBUTE_DATA aRecordAttributes
[] =
874 aAttributeNameFormatData
,
876 aAttributeNameIDData
,
879 CSSM_DB_RECORD_ATTRIBUTE_DATA aRecordAttributeData
=
881 aMetaRecord
.dataRecordType(),
883 sizeof(aRecordAttributes
) / sizeof(CSSM_DB_ATTRIBUTE_DATA
),
886 CssmDbRecordAttributeData
&aRecordData
= CssmDbRecordAttributeData::overlay(aRecordAttributeData
);
888 TrackingAllocator
recordAllocator(CssmAllocator::standard());
889 for (uint32 aRecord
= 0; aRecord
!= aRecordsCount
; aRecord
++)
891 ReadSection aRecordSection
= MetaRecord::readSection(aRecordsSection
, aReadOffset
);
892 uint32 aRecordSize
= aRecordSection
.size();
893 aReadOffset
+= aRecordSize
;
898 aMetaRecord
.unpackRecord(aRecordSection
, recordAllocator
,
899 &aRecordAttributeData
, NULL
, 0);
900 // Create the attribute coresponding to this entry
901 if (aRecordData
[0].size() != 1 || aRecordData
[0].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
)
902 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
903 uint32 aRelationId
= aRecordData
[0];
905 // Skip the schema relations for the meta tables themselves.
906 if (CSSM_DB_RECORDTYPE_SCHEMA_START
<= aRelationId
&& aRelationId
< CSSM_DB_RECORDTYPE_SCHEMA_END
)
909 // Get the MetaRecord corresponding to the specified RelationId
910 MetaRecord
&aMetaRecord
= findTable(aRelationId
).getMetaRecord();
912 if (aRecordData
[1].size() != 1
913 || aRecordData
[1].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
914 || aRecordData
[2].size() != 1
915 || aRecordData
[2].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
916 || aRecordData
[5].size() != 1
917 || aRecordData
[5].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
)
918 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
920 uint32 anAttributeId
= aRecordData
[1];
921 uint32 anAttributeNameFormat
= aRecordData
[2];
922 uint32 anAttributeFormat
= aRecordData
[5];
923 auto_ptr
<string
> aName
;
924 const CssmData
*aNameID
= NULL
;
926 if (aRecordData
[3].size() == 1)
928 if (aRecordData
[3].format() != CSSM_DB_ATTRIBUTE_FORMAT_STRING
)
929 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
931 auto_ptr
<string
> aName2(new string(static_cast<string
>(aRecordData
[3])));
935 if (aRecordData
[4].size() == 1)
937 if (aRecordData
[4].format() != CSSM_DB_ATTRIBUTE_FORMAT_BLOB
)
938 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
940 // @@@ Invoking conversion operator to CssmData & on aRecordData[4]
941 // And taking address of result.
942 aNameID
= &static_cast<CssmData
&>(aRecordData
[4]);
945 // Make sure that the attribute specified by anAttributeNameFormat is present.
946 switch (anAttributeNameFormat
)
948 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING
:
949 if (aRecordData
[3].size() != 1)
950 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
952 case CSSM_DB_ATTRIBUTE_NAME_AS_OID
:
953 if (aRecordData
[4].size() != 1)
954 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
956 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
:
959 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
962 // Create the attribute
963 aMetaRecord
.createAttribute(aName
.get(), aNameID
, anAttributeId
, anAttributeFormat
);
967 aRecordData
.deleteValues(CssmAllocator::standard());
971 aRecordData
.deleteValues(CssmAllocator::standard());
977 // initialize the indexes associated with each table
979 TableMap::iterator it
;
980 for (it
= mTableMap
.begin(); it
!= mTableMap
.end(); it
++)
981 it
->second
->readIndexSection();
986 for_each_map_delete(mTableMap
.begin(), mTableMap
.end());
993 DbVersion::getRecord(Table::Id inTableId
, const RecordId
&inRecordId
,
994 CSSM_DB_RECORD_ATTRIBUTE_DATA
*inoutAttributes
,
996 CssmAllocator
&inAllocator
) const
998 return findTable(inTableId
).getRecord(inRecordId
, inoutAttributes
,
999 inoutData
, inAllocator
);
1003 DbVersion::createCursor(const CSSM_QUERY
*inQuery
) const
1005 // XXX We should add support for these special query types
1006 // By Creating a Cursor that iterates over multiple tables
1007 if (!inQuery
|| inQuery
->RecordType
== CSSM_DL_DB_RECORD_ANY
1008 || inQuery
->RecordType
== CSSM_DL_DB_RECORD_ALL_KEYS
)
1010 return new MultiCursor(inQuery
, *this);
1013 return findTable(inQuery
->RecordType
).createCursor(inQuery
, *this);
1017 DbVersion::findTable(Table::Id inTableId
) const
1019 TableMap::const_iterator it
= mTableMap
.find(inTableId
);
1020 if (it
== mTableMap
.end())
1021 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1026 DbVersion::findTable(Table::Id inTableId
)
1028 TableMap::iterator it
= mTableMap
.find(inTableId
);
1029 if (it
== mTableMap
.end())
1030 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1035 // Cursor implemetation
1043 // LinearCursor implemetation
1045 LinearCursor::LinearCursor(const CSSM_QUERY
*inQuery
, const DbVersion
&inDbVersion
,
1046 const Table
&inTable
) :
1047 mDbVersion(&inDbVersion
),
1048 mRecordsCount(inTable
.getRecordsCount()),
1050 mRecordsSection(inTable
.getRecordsSection()),
1052 mMetaRecord(inTable
.getMetaRecord())
1056 mConjunctive
= inQuery
->Conjunctive
;
1057 mQueryFlags
= inQuery
->QueryFlags
;
1058 // XXX Do something with inQuery->QueryLimits?
1059 uint32 aPredicatesCount
= inQuery
->NumSelectionPredicates
;
1060 mPredicates
.resize(aPredicatesCount
);
1063 for (uint32 anIndex
= 0; anIndex
< aPredicatesCount
; anIndex
++)
1065 CSSM_SELECTION_PREDICATE
&aPredicate
= inQuery
->SelectionPredicate
[anIndex
];
1066 mPredicates
[anIndex
] = new SelectionPredicate(mMetaRecord
, aPredicate
);
1071 for_each_delete(mPredicates
.begin(), mPredicates
.end());
1077 LinearCursor::~LinearCursor()
1079 for_each_delete(mPredicates
.begin(), mPredicates
.end());
1083 LinearCursor::next(Table::Id
&outTableId
,
1084 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
1085 CssmData
*inoutData
, CssmAllocator
&inAllocator
, RecordId
&recordId
)
1087 while (mRecord
++ < mRecordsCount
)
1089 ReadSection aRecordSection
= MetaRecord::readSection(mRecordsSection
, mReadOffset
);
1090 uint32 aRecordSize
= aRecordSection
.size();
1091 mReadOffset
+= aRecordSize
;
1093 PredicateVector::const_iterator anIt
= mPredicates
.begin();
1094 PredicateVector::const_iterator anEnd
= mPredicates
.end();
1098 // If there are no predicates we have a match.
1101 else if (mConjunctive
== CSSM_DB_OR
)
1103 // If mConjunctive is OR, the first predicate that returns
1104 // true indicates a match. Dropthough means no match
1106 for (; anIt
!= anEnd
; anIt
++)
1108 if ((*anIt
)->evaluate(aRecordSection
))
1115 else if (mConjunctive
== CSSM_DB_AND
|| mConjunctive
== CSSM_DB_NONE
)
1117 // If mConjunctive is AND (or NONE), the first predicate that returns
1118 // false indicates a mismatch. Dropthough means a match
1120 for (; anIt
!= anEnd
; anIt
++)
1122 if (!(*anIt
)->evaluate(aRecordSection
))
1131 // XXX Should be CSSMERR_DL_INVALID_QUERY (or CSSMERR_DL_INVALID_CONJUNTIVE).
1132 CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_QUERY
);
1137 // Get the actual record.
1138 mMetaRecord
.unpackRecord(aRecordSection
, inAllocator
,
1139 inoutAttributes
, inoutData
,
1141 outTableId
= mMetaRecord
.dataRecordType();
1142 recordId
= MetaRecord::unpackRecordId(aRecordSection
);
1154 IndexCursor::IndexCursor(DbQueryKey
*queryKey
, const DbVersion
&inDbVersion
,
1155 const Table
&table
, const DbConstIndex
*index
)
1156 : mQueryKey(queryKey
), mDbVersion(inDbVersion
), mTable(table
), mIndex(index
)
1158 index
->performQuery(*queryKey
, mBegin
, mEnd
);
1161 IndexCursor::~IndexCursor()
1163 // the query key will be deleted automatically, since it's an auto_ptr
1167 IndexCursor::next(Table::Id
&outTableId
,
1168 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes
,
1170 CssmAllocator
&inAllocator
, RecordId
&recordId
)
1175 ReadSection rs
= mIndex
->getRecordSection(mBegin
++);
1176 const MetaRecord
&metaRecord
= mTable
.getMetaRecord();
1178 outTableId
= metaRecord
.dataRecordType();
1179 metaRecord
.unpackRecord(rs
, inAllocator
, outAttributes
, outData
, 0);
1181 recordId
= MetaRecord::unpackRecordId(rs
);
1188 MultiCursor::MultiCursor(const CSSM_QUERY
*inQuery
, const DbVersion
&inDbVersion
) :
1189 mDbVersion(&inDbVersion
), mTableIterator(inDbVersion
.begin())
1192 mQuery
.reset(new CssmAutoQuery(*inQuery
));
1195 mQuery
.reset(new CssmAutoQuery());
1196 mQuery
->recordType(CSSM_DL_DB_RECORD_ANY
);
1200 MultiCursor::~MultiCursor()
1205 MultiCursor::next(Table::Id
&outTableId
,
1206 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
1207 CssmData
*inoutData
, CssmAllocator
&inAllocator
, RecordId
&recordId
)
1213 if (mTableIterator
== mDbVersion
->end())
1216 const Table
&aTable
= *mTableIterator
++;
1217 if (!aTable
.matchesTableId(mQuery
->recordType()))
1220 mCursor
.reset(aTable
.createCursor(mQuery
.get(), *mDbVersion
));
1223 if (mCursor
->next(outTableId
, inoutAttributes
, inoutData
, inAllocator
, recordId
))
1226 mCursor
.reset(NULL
);
1234 DbModifier::DbModifier(AtomicFile
&inAtomicFile
, const AppleDatabase
&db
) :
1237 mAtomicFile(inAtomicFile
),
1243 DbModifier::~DbModifier()
1247 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1255 const RefPointer
<const DbVersion
>
1256 DbModifier::getDbVersion()
1258 StLock
<Mutex
> _(mDbVersionLock
);
1259 if (mDbVersion
&& mDbVersion
->isDirty())
1262 if (mDbVersion
== NULL
)
1263 mDbVersion
= new DbVersion(mAtomicFile
, mDb
);
1269 DbModifier::createDatabase(const CSSM_DBINFO
&inDbInfo
,
1270 const CSSM_ACL_ENTRY_INPUT
*inInitialAclEntry
)
1272 // XXX This needs better locking. There is a possible race condition between
1273 // two concurrent creators. Or a writer/creator or a close/create etc.
1274 if (mWriting
|| !mModifiedTableMap
.empty())
1275 CssmError::throwMe(CSSMERR_DL_DATASTORE_ALREADY_EXISTS
);
1277 mVersionId
= mAtomicFile
.enterCreate(mFileRef
);
1280 // we need to create the meta tables first, because inserting tables
1281 // (including the meta tables themselves) relies on them being there
1282 createTable(new MetaRecord(mDb
.schemaRelations
));
1283 createTable(new MetaRecord(mDb
.schemaAttributes
));
1284 createTable(new MetaRecord(mDb
.schemaIndexes
));
1285 createTable(new MetaRecord(mDb
.schemaParsingModule
));
1287 // now add the meta-tables' schema to the meta tables themselves
1288 insertTableSchema(mDb
.schemaRelations
);
1289 insertTableSchema(mDb
.schemaAttributes
);
1290 insertTableSchema(mDb
.schemaIndexes
);
1291 insertTableSchema(mDb
.schemaParsingModule
);
1293 if (inInitialAclEntry
!= NULL
)
1295 //createACL(*inInitialAclEntry);
1298 if (inDbInfo
.NumberOfRecordTypes
== 0)
1300 if (inDbInfo
.RecordAttributeNames
== NULL
)
1301 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1302 if (inDbInfo
.RecordIndexes
== NULL
)
1303 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_INDEX
);
1304 if (inDbInfo
.DefaultParsingModules
== NULL
)
1305 CssmError::throwMe(CSSMERR_DL_INVALID_PARSING_MODULE
);
1307 for (uint32 anIndex
= 0; anIndex
< inDbInfo
.NumberOfRecordTypes
; anIndex
++)
1309 insertTable(CssmDbRecordAttributeInfo::overlay(inDbInfo
.RecordAttributeNames
[anIndex
]),
1310 &inDbInfo
.RecordIndexes
[anIndex
],
1311 &inDbInfo
.DefaultParsingModules
[anIndex
]);
1315 void DbModifier::openDatabase()
1317 commit(); // XXX Requires write lock.
1321 void DbModifier::closeDatabase()
1323 commit(); // XXX Requires write lock.
1324 StLock
<Mutex
> _(mDbVersionLock
);
1328 void DbModifier::deleteDatabase()
1330 rollback(); // XXX Requires write lock. Also if autoCommit was disabled
1331 // this will incorrectly cause the performDelete to throw CSSMERR_DB_DOES_NOT_EXIST.
1332 StLock
<Mutex
> _(mDbVersionLock
);
1334 mAtomicFile
.performDelete();
1338 DbModifier::modifyDatabase()
1345 const uint8
*aFileAddress
;
1347 mVersionId
= mAtomicFile
.enterWrite(aFileAddress
, aLength
, mFileRef
);
1350 // Aquire the mutex protecting mDbVersion
1351 StLock
<Mutex
> _l(mDbVersionLock
);
1352 if (mDbVersion
== nil
|| mDbVersion
->getVersionId() != mVersionId
)
1354 // This will call enterRead(). Now that we hold the write
1355 // lock on the file this ensures we get the same verison
1356 // enterWrite just returned.
1357 mDbVersion
= new DbVersion(mAtomicFile
, mDb
);
1361 // Remove all old modified tables
1362 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1363 mModifiedTableMap
.clear();
1365 // Setup the new tables
1366 DbVersion::TableMap::const_iterator anIt
=
1367 mDbVersion
->mTableMap
.begin();
1368 DbVersion::TableMap::const_iterator anEnd
=
1369 mDbVersion
->mTableMap
.end();
1370 for (; anIt
!= anEnd
; ++anIt
)
1372 auto_ptr
<ModifiedTable
> aTable(new ModifiedTable(anIt
->second
));
1373 mModifiedTableMap
.insert(ModifiedTableMap::value_type(anIt
->first
,
1380 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1381 mModifiedTableMap
.clear();
1388 DbModifier::deleteRecord(Table::Id inTableId
, const RecordId
&inRecordId
)
1391 findTable(inTableId
).deleteRecord(inRecordId
);
1395 DbModifier::insertRecord(Table::Id inTableId
,
1396 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
1397 const CssmData
*inData
)
1400 return findTable(inTableId
).insertRecord(mVersionId
, inAttributes
, inData
);
1404 DbModifier::updateRecord(Table::Id inTableId
, const RecordId
&inRecordId
,
1405 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
1406 const CssmData
*inData
,
1407 CSSM_DB_MODIFY_MODE inModifyMode
)
1409 commit(); // XXX this is not thread safe, but what is?
1411 return findTable(inTableId
).updateRecord(inRecordId
, inAttributes
, inData
, inModifyMode
);
1414 // Create a table associated with a given metarecord, and add the table
1418 DbModifier::createTable(MetaRecord
*inMetaRecord
)
1420 auto_ptr
<MetaRecord
> aMetaRecord(inMetaRecord
);
1421 auto_ptr
<ModifiedTable
> aModifiedTable(new ModifiedTable(inMetaRecord
));
1422 // Now that aModifiedTable is fully constructed it owns inMetaRecord
1423 aMetaRecord
.release();
1425 if (!mModifiedTableMap
.insert
1426 (ModifiedTableMap::value_type(inMetaRecord
->dataRecordType(),
1427 aModifiedTable
.get())).second
)
1429 // XXX Should be CSSMERR_DL_DUPLICATE_RECORDTYPE. Since that
1430 // doesn't exist we report that the metatable's unique index would
1431 // no longer be valid
1432 CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
);
1435 return aModifiedTable
.release();
1439 DbModifier::deleteTable(Table::Id inTableId
)
1442 // Can't delete schema tables.
1443 if (CSSM_DB_RECORDTYPE_SCHEMA_START
<= inTableId
1444 && inTableId
< CSSM_DB_RECORDTYPE_SCHEMA_END
)
1445 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1447 // Find the ModifiedTable and delete it
1448 ModifiedTableMap::iterator it
= mModifiedTableMap
.find(inTableId
);
1449 if (it
== mModifiedTableMap
.end())
1450 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1453 mModifiedTableMap
.erase(it
);
1457 DbModifier::writeAuthSection(uint32 inSectionOffset
)
1459 WriteSection anAuthSection
;
1461 // XXX Put real data into the authsection.
1462 uint32 anOffset
= anAuthSection
.put(0, 0);
1463 anAuthSection
.size(anOffset
);
1465 mAtomicFile
.write(AtomicFile::FromStart
, inSectionOffset
,
1466 anAuthSection
.address(), anAuthSection
.size());
1467 return inSectionOffset
+ anOffset
;
1471 DbModifier::writeSchemaSection(uint32 inSectionOffset
)
1473 uint32 aTableCount
= mModifiedTableMap
.size();
1474 WriteSection
aTableSection(CssmAllocator::standard(),
1475 OffsetTables
+ AtomSize
* aTableCount
);
1476 // Set aTableSection to the correct size.
1477 aTableSection
.size(OffsetTables
+ AtomSize
* aTableCount
);
1478 aTableSection
.put(OffsetTablesCount
, aTableCount
);
1480 uint32 anOffset
= inSectionOffset
+ OffsetTables
+ AtomSize
* aTableCount
;
1481 ModifiedTableMap::const_iterator anIt
= mModifiedTableMap
.begin();
1482 ModifiedTableMap::const_iterator anEnd
= mModifiedTableMap
.end();
1483 for (uint32 aTableNumber
= 0; anIt
!= anEnd
; anIt
++, aTableNumber
++)
1485 // Put the offset to the current table relative to the start of
1486 // this section into the tables array
1487 aTableSection
.put(OffsetTables
+ AtomSize
* aTableNumber
,
1488 anOffset
- inSectionOffset
);
1489 anOffset
= anIt
->second
->writeTable(mAtomicFile
, anOffset
);
1492 aTableSection
.put(OffsetSchemaSize
, anOffset
- inSectionOffset
);
1493 mAtomicFile
.write(AtomicFile::FromStart
, inSectionOffset
,
1494 aTableSection
.address(), aTableSection
.size());
1500 DbModifier::commit()
1506 WriteSection
aHeaderSection(CssmAllocator::standard(), HeaderSize
);
1507 // Set aHeaderSection to the correct size.
1508 aHeaderSection
.size(HeaderSize
);
1510 // Start writing sections after the header
1511 uint32 anOffset
= HeaderOffset
+ HeaderSize
;
1513 // Write auth section
1514 aHeaderSection
.put(OffsetAuthOffset
, anOffset
);
1515 anOffset
= writeAuthSection(anOffset
);
1516 // Write schema section
1517 aHeaderSection
.put(OffsetSchemaOffset
, anOffset
);
1518 anOffset
= writeSchemaSection(anOffset
);
1520 // Write out the file header.
1521 aHeaderSection
.put(OffsetMagic
, HeaderMagic
);
1522 aHeaderSection
.put(OffsetVersion
, HeaderVersion
);
1523 mAtomicFile
.write(AtomicFile::FromStart
, HeaderOffset
,
1524 aHeaderSection
.address(), aHeaderSection
.size());
1530 rollback(); // Sets mWriting to false;
1537 mAtomicFile
.commit();
1541 DbModifier::rollback()
1546 mAtomicFile
.rollback();
1551 DbModifier::getRecord(Table::Id inTableId
, const RecordId
&inRecordId
,
1552 CSSM_DB_RECORD_ATTRIBUTE_DATA
*inoutAttributes
,
1553 CssmData
*inoutData
, CssmAllocator
&inAllocator
)
1555 // XXX never call commit(), rather search our own record tables.
1556 commit(); // XXX Requires write lock.
1557 return getDbVersion()->getRecord(inTableId
, inRecordId
,
1558 inoutAttributes
, inoutData
, inAllocator
);
1562 DbModifier::createCursor(const CSSM_QUERY
*inQuery
)
1564 // XXX Be smarter as to when we must call commit (i.e. don't
1565 // force commit if the table being queried has not been modified).
1566 commit(); // XXX Requires write lock.
1567 return getDbVersion()->createCursor(inQuery
);
1570 // Insert schema records for a new table into the metatables of the database. This gets
1571 // called while a database is being created.
1574 DbModifier::insertTableSchema(const CssmDbRecordAttributeInfo
&inInfo
,
1575 const CSSM_DB_RECORD_INDEX_INFO
*inIndexInfo
/* = NULL */)
1577 ModifiedTable
&aTable
= findTable(inInfo
.DataRecordType
);
1578 const MetaRecord
&aMetaRecord
= aTable
.getMetaRecord();
1580 CssmAutoDbRecordAttributeData
aRecordBuilder(5); // Set capacity to 5 so we don't need to grow
1582 // Create the entry for the SchemaRelations table.
1583 aRecordBuilder
.add(RelationID
, inInfo
.recordType());
1584 aRecordBuilder
.add(RelationName
, mDb
.recordName(inInfo
.recordType()));
1586 // Insert the record into the SchemaRelations ModifiedTable
1587 findTable(mDb
.schemaRelations
.DataRecordType
).insertRecord(mVersionId
,
1588 &aRecordBuilder
, NULL
);
1590 ModifiedTable
&anAttributeTable
= findTable(mDb
.schemaAttributes
.DataRecordType
);
1591 for (uint32 anIndex
= 0; anIndex
< inInfo
.size(); anIndex
++)
1593 // Create an entry for the SchemaAttributes table.
1594 aRecordBuilder
.clear();
1595 aRecordBuilder
.add(RelationID
, inInfo
.recordType());
1596 aRecordBuilder
.add(AttributeNameFormat
, inInfo
.at(anIndex
).nameFormat());
1598 uint32 attributeId
= aMetaRecord
.metaAttribute(inInfo
.at(anIndex
)).attributeId();
1600 switch (inInfo
.at(anIndex
).nameFormat())
1602 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING
:
1603 aRecordBuilder
.add(AttributeName
, inInfo
.at(anIndex
).Label
.AttributeName
);
1605 case CSSM_DB_ATTRIBUTE_NAME_AS_OID
:
1606 aRecordBuilder
.add(AttributeNameID
, inInfo
.at(anIndex
).Label
.AttributeOID
);
1608 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
:
1611 CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME
);
1614 aRecordBuilder
.add(AttributeID
, attributeId
);
1615 aRecordBuilder
.add(AttributeFormat
, inInfo
.at(anIndex
).format());
1617 // Insert the record into the SchemaAttributes ModifiedTable
1618 anAttributeTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1621 if (inIndexInfo
!= NULL
) {
1623 if (inIndexInfo
->DataRecordType
!= inInfo
.DataRecordType
&&
1624 inIndexInfo
->NumberOfIndexes
> 0)
1625 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1627 ModifiedTable
&indexMetaTable
= findTable(mDb
.schemaIndexes
.DataRecordType
);
1628 uint32 aNumberOfIndexes
= inIndexInfo
->NumberOfIndexes
;
1630 for (uint32 anIndex
= 0; anIndex
< aNumberOfIndexes
; anIndex
++)
1632 const CssmDbIndexInfo
&thisIndex
= CssmDbIndexInfo::overlay(inIndexInfo
->IndexInfo
[anIndex
]);
1634 // make sure the index is supported
1635 if (thisIndex
.dataLocation() != CSSM_DB_INDEX_ON_ATTRIBUTE
)
1636 CssmError::throwMe(CSSMERR_DL_INVALID_INDEX_INFO
);
1638 // assign an index ID: the unique index is ID 0, all others are ID > 0
1640 if (thisIndex
.IndexType
== CSSM_DB_INDEX_UNIQUE
)
1643 indexId
= anIndex
+ 1;
1645 // figure out the attribute ID
1646 uint32 attributeId
=
1647 aMetaRecord
.metaAttribute(thisIndex
.Info
).attributeId();
1649 // Create an entry for the SchemaIndexes table.
1650 aRecordBuilder
.clear();
1651 aRecordBuilder
.add(RelationID
, inInfo
.DataRecordType
);
1652 aRecordBuilder
.add(IndexID
, indexId
);
1653 aRecordBuilder
.add(AttributeID
, attributeId
);
1654 aRecordBuilder
.add(IndexType
, thisIndex
.IndexType
);
1655 aRecordBuilder
.add(IndexedDataLocation
, thisIndex
.IndexedDataLocation
);
1657 // Insert the record into the SchemaIndexes ModifiedTable
1658 indexMetaTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1660 // update the table's index objects
1661 DbMutableIndex
&index
= aTable
.findIndex(indexId
, aMetaRecord
, indexId
== 0);
1662 index
.appendAttribute(attributeId
);
1667 // Insert a new table. The attribute info is required; the index and parsing module
1668 // descriptions are optional. This version gets called during the creation of a
1672 DbModifier::insertTable(const CssmDbRecordAttributeInfo
&inInfo
,
1673 const CSSM_DB_RECORD_INDEX_INFO
*inIndexInfo
/* = NULL */,
1674 const CSSM_DB_PARSING_MODULE_INFO
*inParsingModule
/* = NULL */)
1677 createTable(new MetaRecord(inInfo
));
1678 insertTableSchema(inInfo
, inIndexInfo
);
1681 // Insert a new table. This is the version that gets called when a table is added
1682 // after a database has been created.
1685 DbModifier::insertTable(Table::Id inTableId
, const string
&inTableName
,
1686 uint32 inNumberOfAttributes
,
1687 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
*inAttributeInfo
,
1688 uint32 inNumberOfIndexes
,
1689 const CSSM_DB_SCHEMA_INDEX_INFO
*inIndexInfo
)
1692 ModifiedTable
*aTable
= createTable(new MetaRecord(inTableId
, inNumberOfAttributes
, inAttributeInfo
));
1694 CssmAutoDbRecordAttributeData
aRecordBuilder(6); // Set capacity to 6 so we don't need to grow
1696 // Create the entry for the SchemaRelations table.
1697 aRecordBuilder
.add(RelationID
, inTableId
);
1698 aRecordBuilder
.add(RelationName
, inTableName
);
1700 // Insert the record into the SchemaRelations ModifiedTable
1701 findTable(mDb
.schemaRelations
.DataRecordType
).insertRecord(mVersionId
,
1702 &aRecordBuilder
, NULL
);
1704 ModifiedTable
&anAttributeTable
= findTable(mDb
.schemaAttributes
.DataRecordType
);
1705 for (uint32 anIndex
= 0; anIndex
< inNumberOfAttributes
; anIndex
++)
1707 // Create an entry for the SchemaAttributes table.
1708 aRecordBuilder
.clear();
1709 aRecordBuilder
.add(RelationID
, inTableId
);
1710 // XXX What should this be? We set it to CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER for now
1711 // since the AttributeID is always valid.
1712 aRecordBuilder
.add(AttributeNameFormat
, uint32(CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
));
1713 aRecordBuilder
.add(AttributeID
, inAttributeInfo
[anIndex
].AttributeId
);
1714 if (inAttributeInfo
[anIndex
].AttributeName
)
1715 aRecordBuilder
.add(AttributeName
, inAttributeInfo
[anIndex
].AttributeName
);
1716 if (inAttributeInfo
[anIndex
].AttributeNameID
.Length
> 0)
1717 aRecordBuilder
.add(AttributeNameID
, inAttributeInfo
[anIndex
].AttributeNameID
);
1718 aRecordBuilder
.add(AttributeFormat
, inAttributeInfo
[anIndex
].DataType
);
1720 // Insert the record into the SchemaAttributes ModifiedTable
1721 anAttributeTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1724 ModifiedTable
&anIndexTable
= findTable(mDb
.schemaIndexes
.DataRecordType
);
1725 for (uint32 anIndex
= 0; anIndex
< inNumberOfIndexes
; anIndex
++)
1727 // Create an entry for the SchemaIndexes table.
1728 aRecordBuilder
.clear();
1729 aRecordBuilder
.add(RelationID
, inTableId
);
1730 aRecordBuilder
.add(IndexID
, inIndexInfo
[anIndex
].IndexId
);
1731 aRecordBuilder
.add(AttributeID
, inIndexInfo
[anIndex
].AttributeId
);
1732 aRecordBuilder
.add(IndexType
, inIndexInfo
[anIndex
].IndexType
);
1733 aRecordBuilder
.add(IndexedDataLocation
, inIndexInfo
[anIndex
].IndexedDataLocation
);
1735 // Insert the record into the SchemaIndexes ModifiedTable
1736 anIndexTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1738 // update the table's index objects
1739 DbMutableIndex
&index
= aTable
->findIndex(inIndexInfo
[anIndex
].IndexId
,
1740 aTable
->getMetaRecord(), inIndexInfo
[anIndex
].IndexType
== CSSM_DB_INDEX_UNIQUE
);
1741 index
.appendAttribute(inIndexInfo
[anIndex
].AttributeId
);
1746 DbModifier::findTable(Table::Id inTableId
)
1748 ModifiedTableMap::iterator it
= mModifiedTableMap
.find(inTableId
);
1749 if (it
== mModifiedTableMap
.end())
1750 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1756 // AppleDatabaseManager implementation
1759 AppleDatabaseManager::AppleDatabaseManager(const AppleDatabaseTableName
*tableNames
)
1760 : DatabaseManager(),
1761 mTableNames(tableNames
)
1763 // make sure that a proper set of table ids and names has been provided
1766 CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR
);
1769 for (i
= 0; mTableNames
[i
].mTableName
; i
++) {}
1770 if (i
< AppleDatabaseTableName::kNumRequiredTableNames
)
1771 CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR
);
1776 AppleDatabaseManager::make(const DbName
&inDbName
)
1778 return new AppleDatabase(inDbName
, mTableNames
);
1782 // AppleDbContext implementation
1784 AppleDbContext::AppleDbContext(Database
&inDatabase
,
1785 DatabaseSession
&inDatabaseSession
,
1786 CSSM_DB_ACCESS_TYPE inAccessRequest
,
1787 const AccessCredentials
*inAccessCred
,
1788 const void *inOpenParameters
) :
1789 DbContext(inDatabase
, inDatabaseSession
, inAccessRequest
, inAccessCred
)
1791 const CSSM_APPLEDL_OPEN_PARAMETERS
*anOpenParameters
=
1792 reinterpret_cast<const CSSM_APPLEDL_OPEN_PARAMETERS
*>(inOpenParameters
);
1793 if (anOpenParameters
)
1795 if (anOpenParameters
->length
< sizeof(CSSM_APPLEDL_OPEN_PARAMETERS
)
1796 || anOpenParameters
->version
!= 0)
1797 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS
);
1799 mAutoCommit
= anOpenParameters
->autoCommit
== CSSM_FALSE
? false : true;
1805 AppleDbContext::~AppleDbContext()
1810 // AppleDatabase implementation
1812 AppleDatabase::AppleDatabase(const DbName
&inDbName
, const AppleDatabaseTableName
*tableNames
) :
1814 schemaRelations(tableNames
[AppleDatabaseTableName::kSchemaInfo
].mTableId
,
1815 sizeof(AttrSchemaRelations
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
1816 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaRelations
)),
1817 schemaAttributes(tableNames
[AppleDatabaseTableName::kSchemaAttributes
].mTableId
,
1818 sizeof(AttrSchemaAttributes
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
1819 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaAttributes
)),
1820 schemaIndexes(tableNames
[AppleDatabaseTableName::kSchemaIndexes
].mTableId
,
1821 sizeof(AttrSchemaIndexes
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
1822 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaIndexes
)),
1823 schemaParsingModule(tableNames
[AppleDatabaseTableName::kSchemaParsingModule
].mTableId
,
1824 sizeof(AttrSchemaParsingModule
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
1825 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaParsingModule
)),
1826 mAtomicFile(mDbName
),
1827 mDbModifier(mAtomicFile
, *this),
1828 mTableNames(tableNames
)
1832 AppleDatabase::~AppleDatabase()
1836 // Return the name of a record type. This uses a table that maps record types
1837 // to record names. The table is provided when the database is created.
1839 const char *AppleDatabase::recordName(CSSM_DB_RECORDTYPE inRecordType
) const
1841 if (inRecordType
== CSSM_DL_DB_RECORD_ANY
|| inRecordType
== CSSM_DL_DB_RECORD_ALL_KEYS
)
1842 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1844 for (uint32 i
= 0; mTableNames
[i
].mTableName
; i
++)
1845 if (mTableNames
[i
].mTableId
== inRecordType
)
1846 return mTableNames
[i
].mTableName
;
1852 AppleDatabase::makeDbContext(DatabaseSession
&inDatabaseSession
,
1853 CSSM_DB_ACCESS_TYPE inAccessRequest
,
1854 const AccessCredentials
*inAccessCred
,
1855 const void *inOpenParameters
)
1857 return new AppleDbContext(*this, inDatabaseSession
, inAccessRequest
,
1858 inAccessCred
, inOpenParameters
);
1862 AppleDatabase::dbCreate(DbContext
&inDbContext
, const CSSM_DBINFO
&inDBInfo
,
1863 const CSSM_ACL_ENTRY_INPUT
*inInitialAclEntry
)
1867 StLock
<Mutex
> _(mWriteLock
);
1868 mDbModifier
.createDatabase(inDBInfo
, inInitialAclEntry
);
1872 mDbModifier
.rollback();
1875 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
1876 mDbModifier
.commit();
1880 AppleDatabase::dbOpen(DbContext
&inDbContext
)
1882 mDbModifier
.openDatabase();
1886 AppleDatabase::dbClose()
1888 StLock
<Mutex
> _(mWriteLock
);
1889 mDbModifier
.closeDatabase();
1893 AppleDatabase::dbDelete(DatabaseSession
&inDatabaseSession
,
1894 const AccessCredentials
*inAccessCred
)
1896 StLock
<Mutex
> _(mWriteLock
);
1897 // XXX Check callers credentials.
1898 mDbModifier
.deleteDatabase();
1902 AppleDatabase::createRelation(DbContext
&inDbContext
,
1903 CSSM_DB_RECORDTYPE inRelationID
,
1904 const char *inRelationName
,
1905 uint32 inNumberOfAttributes
,
1906 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
&inAttributeInfo
,
1907 uint32 inNumberOfIndexes
,
1908 const CSSM_DB_SCHEMA_INDEX_INFO
&inIndexInfo
)
1912 StLock
<Mutex
> _(mWriteLock
);
1913 // XXX Fix the refs here.
1914 mDbModifier
.insertTable(inRelationID
, inRelationName
,
1915 inNumberOfAttributes
, &inAttributeInfo
,
1916 inNumberOfIndexes
, &inIndexInfo
);
1920 mDbModifier
.rollback();
1923 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
1924 mDbModifier
.commit();
1928 AppleDatabase::destroyRelation(DbContext
&inDbContext
,
1929 CSSM_DB_RECORDTYPE inRelationID
)
1933 StLock
<Mutex
> _(mWriteLock
);
1934 mDbModifier
.deleteTable(inRelationID
);
1938 mDbModifier
.rollback();
1941 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
1942 mDbModifier
.commit();
1946 AppleDatabase::authenticate(DbContext
&inDbContext
,
1947 CSSM_DB_ACCESS_TYPE inAccessRequest
,
1948 const AccessCredentials
&inAccessCred
)
1950 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
1954 AppleDatabase::getDbAcl(DbContext
&inDbContext
,
1955 const CSSM_STRING
*inSelectionTag
,
1956 uint32
&outNumberOfAclInfos
,
1957 CSSM_ACL_ENTRY_INFO_PTR
&outAclInfos
)
1959 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
1963 AppleDatabase::changeDbAcl(DbContext
&inDbContext
,
1964 const AccessCredentials
&inAccessCred
,
1965 const CSSM_ACL_EDIT
&inAclEdit
)
1967 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
1971 AppleDatabase::getDbOwner(DbContext
&inDbContext
,
1972 CSSM_ACL_OWNER_PROTOTYPE
&outOwner
)
1974 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
1978 AppleDatabase::changeDbOwner(DbContext
&inDbContext
,
1979 const AccessCredentials
&inAccessCred
,
1980 const CSSM_ACL_OWNER_PROTOTYPE
&inNewOwner
)
1982 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
1986 AppleDatabase::getDbNameFromHandle(const DbContext
&inDbContext
) const
1988 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
1991 CSSM_DB_UNIQUE_RECORD_PTR
1992 AppleDatabase::dataInsert(DbContext
&inDbContext
,
1993 CSSM_DB_RECORDTYPE inRecordType
,
1994 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
1995 const CssmData
*inData
)
1997 CSSM_DB_UNIQUE_RECORD_PTR anUniqueRecordPtr
= NULL
;
2000 StLock
<Mutex
> _(mWriteLock
);
2001 const RecordId aRecordId
=
2002 mDbModifier
.insertRecord(inRecordType
, inAttributes
, inData
);
2004 anUniqueRecordPtr
= createUniqueRecord(inDbContext
, inRecordType
,
2006 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2007 mDbModifier
.commit();
2011 if (anUniqueRecordPtr
!= NULL
)
2012 freeUniqueRecord(inDbContext
, *anUniqueRecordPtr
);
2014 mDbModifier
.rollback();
2018 return anUniqueRecordPtr
;
2022 AppleDatabase::dataDelete(DbContext
&inDbContext
,
2023 const CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
)
2027 StLock
<Mutex
> _(mWriteLock
);
2029 const RecordId
aRecordId(parseUniqueRecord(inUniqueRecord
, aTableId
));
2030 mDbModifier
.deleteRecord(aTableId
, aRecordId
);
2034 mDbModifier
.rollback();
2038 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2039 mDbModifier
.commit();
2043 AppleDatabase::dataModify(DbContext
&inDbContext
,
2044 CSSM_DB_RECORDTYPE inRecordType
,
2045 CSSM_DB_UNIQUE_RECORD
&inoutUniqueRecord
,
2046 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributesToBeModified
,
2047 const CssmData
*inDataToBeModified
,
2048 CSSM_DB_MODIFY_MODE inModifyMode
)
2052 StLock
<Mutex
> _(mWriteLock
);
2054 const RecordId aRecordId
=
2055 mDbModifier
.updateRecord(aTableId
,
2056 parseUniqueRecord(inoutUniqueRecord
, aTableId
),
2057 inAttributesToBeModified
,
2060 updateUniqueRecord(inDbContext
, inRecordType
, aRecordId
, inoutUniqueRecord
);
2064 mDbModifier
.rollback();
2068 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2069 mDbModifier
.commit();
2073 AppleDatabase::dataGetFirst(DbContext
&inDbContext
,
2074 const DLQuery
*inQuery
,
2075 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
2076 CssmData
*inoutData
,
2077 CSSM_DB_UNIQUE_RECORD_PTR
&outUniqueRecord
)
2079 // XXX: register Cursor with DbContext and have DbContext call
2080 // dataAbortQuery for all outstanding Query objects on close.
2081 auto_ptr
<Cursor
> aCursor(mDbModifier
.createCursor(inQuery
));
2085 if (!aCursor
->next(aTableId
, inoutAttributes
, inoutData
,
2086 inDbContext
.mDatabaseSession
, aRecordId
))
2087 // return a NULL handle, and implicitly delete the cursor
2090 outUniqueRecord
= createUniqueRecord(inDbContext
, aTableId
, aRecordId
);
2091 return aCursor
.release()->handle(); // We didn't throw so keep the Cursor around.
2095 AppleDatabase::dataGetNext(DbContext
&inDbContext
,
2096 CSSM_HANDLE inResultsHandle
,
2097 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
2098 CssmData
*inoutData
,
2099 CSSM_DB_UNIQUE_RECORD_PTR
&outUniqueRecord
)
2101 auto_ptr
<Cursor
> aCursor(&findHandle
<Cursor
>(inResultsHandle
, CSSMERR_DL_INVALID_RESULTS_HANDLE
));
2105 if (!aCursor
->next(aTableId
, inoutAttributes
, inoutData
, inDbContext
.mDatabaseSession
, aRecordId
))
2108 outUniqueRecord
= createUniqueRecord(inDbContext
, aTableId
, aRecordId
);
2115 AppleDatabase::dataAbortQuery(DbContext
&inDbContext
,
2116 CSSM_HANDLE inResultsHandle
)
2118 delete &findHandle
<Cursor
>(inResultsHandle
, CSSMERR_DL_INVALID_RESULTS_HANDLE
);
2122 AppleDatabase::dataGetFromUniqueRecordId(DbContext
&inDbContext
,
2123 const CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
,
2124 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
2125 CssmData
*inoutData
)
2128 const RecordId
aRecordId(parseUniqueRecord(inUniqueRecord
, aTableId
));
2129 // XXX Change CDSA spec to use new RecordId returned by this function
2130 mDbModifier
.getRecord(aTableId
, aRecordId
, inoutAttributes
, inoutData
,
2131 inDbContext
.mDatabaseSession
);
2135 AppleDatabase::freeUniqueRecord(DbContext
&inDbContext
,
2136 CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
)
2138 if (inUniqueRecord
.RecordIdentifier
.Length
!= 0
2139 && inUniqueRecord
.RecordIdentifier
.Data
!= NULL
)
2141 inUniqueRecord
.RecordIdentifier
.Length
= 0;
2142 inDbContext
.mDatabaseSession
.free(inUniqueRecord
.RecordIdentifier
.Data
);
2144 inDbContext
.mDatabaseSession
.free(&inUniqueRecord
);
2148 AppleDatabase::updateUniqueRecord(DbContext
&inDbContext
,
2149 CSSM_DB_RECORDTYPE inTableId
,
2150 const RecordId
&inRecordId
,
2151 CSSM_DB_UNIQUE_RECORD
&inoutUniqueRecord
)
2153 uint32
*aBuffer
= reinterpret_cast<uint32
*>(inoutUniqueRecord
.RecordIdentifier
.Data
);
2154 aBuffer
[0] = inTableId
;
2155 aBuffer
[1] = inRecordId
.mRecordNumber
;
2156 aBuffer
[2] = inRecordId
.mCreateVersion
;
2157 aBuffer
[3] = inRecordId
.mRecordVersion
;
2160 CSSM_DB_UNIQUE_RECORD_PTR
2161 AppleDatabase::createUniqueRecord(DbContext
&inDbContext
,
2162 CSSM_DB_RECORDTYPE inTableId
,
2163 const RecordId
&inRecordId
)
2165 CSSM_DB_UNIQUE_RECORD_PTR aUniqueRecord
=
2166 inDbContext
.mDatabaseSession
.alloc
<CSSM_DB_UNIQUE_RECORD
>();
2167 memset(aUniqueRecord
, 0, sizeof(*aUniqueRecord
));
2168 aUniqueRecord
->RecordIdentifier
.Length
= sizeof(uint32
) * 4;
2171 aUniqueRecord
->RecordIdentifier
.Data
=
2172 inDbContext
.mDatabaseSession
.alloc
<uint8
>(sizeof(uint32
) * 4);
2173 updateUniqueRecord(inDbContext
, inTableId
, inRecordId
, *aUniqueRecord
);
2177 inDbContext
.mDatabaseSession
.free(aUniqueRecord
);
2181 return aUniqueRecord
;
2185 AppleDatabase::parseUniqueRecord(const CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
,
2186 CSSM_DB_RECORDTYPE
&outTableId
)
2188 if (inUniqueRecord
.RecordIdentifier
.Length
!= sizeof(uint32
) * 4)
2189 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID
);
2191 uint32
*aBuffer
= reinterpret_cast<uint32
*>(inUniqueRecord
.RecordIdentifier
.Data
);
2192 outTableId
= aBuffer
[0];
2193 return RecordId(aBuffer
[1], aBuffer
[2], aBuffer
[3]);
2197 AppleDatabase::passThrough(DbContext
&dbContext
,
2198 uint32 passThroughId
,
2199 const void *inputParams
,
2200 void **outputParams
)
2202 switch (passThroughId
)
2204 case CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
:
2206 CSSM_BOOL on
= reinterpret_cast<CSSM_BOOL
>(inputParams
);
2207 safer_cast
<AppleDbContext
&>(dbContext
).autoCommit(on
);
2211 case CSSM_APPLEFILEDL_COMMIT
:
2212 mDbModifier
.commit();
2215 case CSSM_APPLEFILEDL_ROLLBACK
:
2216 mDbModifier
.rollback();
2220 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);