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 #if RECORDVERSIONCHECK
206 const RecordId aRecordId
= MetaRecord::unpackRecordId(mTable
->getRecordSection(aRecordNumber
));
207 if (aRecordId
.mRecordVersion
!= inRecordId
.mRecordVersion
)
208 CssmError::throwMe(CSSMERR_DL_RECORD_MODIFIED
);
211 // Schedule the record for deletion
212 if (!mDeletedSet
.insert(aRecordNumber
).second
)
213 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // It was already deleted
217 const RecordId aRecordId
= MetaRecord::unpackRecordId(*anIt
->second
);
218 if (aRecordId
.mCreateVersion
!= inRecordId
.mCreateVersion
)
219 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
221 #if RECORDVERSIONCHECK
222 if (aRecordId
.mRecordVersion
!= inRecordId
.mRecordVersion
)
223 CssmError::throwMe(CSSMERR_DL_RECORD_MODIFIED
);
226 // Remove the inserted (but uncommited) record. It should already be in mDeletedSet
227 // if it existed previously in mTable.
228 mInsertedMap
.erase(anIt
);
234 ModifiedTable::insertRecord(AtomicFile::VersionId inVersionId
,
235 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
236 const CssmData
*inData
)
240 auto_ptr
<WriteSection
> aWriteSection(new WriteSection());
241 getMetaRecord().packRecord(*aWriteSection
, inAttributes
, inData
);
242 uint32 aRecordNumber
= nextRecordNumber();
244 // add the record to all the indexes; this will throw if the new record
245 // violates a unique index
246 MutableIndexMap::iterator it
;
247 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
248 it
->second
->insertRecord(aRecordNumber
, *(aWriteSection
.get()));
250 // schedule the record for insertion
251 RecordId
aRecordId(aRecordNumber
, inVersionId
);
252 MetaRecord::packRecordId(aRecordId
, *aWriteSection
);
253 mInsertedMap
.insert(InsertedMap::value_type(aRecordNumber
, aWriteSection
.get()));
255 aWriteSection
.release();
261 ModifiedTable::updateRecord(const RecordId
&inRecordId
,
262 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
263 const CssmData
*inData
,
264 CSSM_DB_MODIFY_MODE inModifyMode
)
268 uint32 aRecordNumber
= inRecordId
.mRecordNumber
;
269 InsertedMap::iterator anIt
= mInsertedMap
.find(inRecordId
.mRecordNumber
);
271 // aReUpdate is true iff we are updating an already updated record.
272 bool aReUpdate
= anIt
!= mInsertedMap
.end();
274 // If we are not re-updating and there is no old table than this record does not exist yet.
275 if (!aReUpdate
&& !mTable
)
276 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
278 const ReadSection
&anOldDbRecord
= aReUpdate
? *anIt
->second
: mTable
->getRecordSection(aRecordNumber
);
279 const RecordId aRecordId
= MetaRecord::unpackRecordId(anOldDbRecord
);
281 // Did someone else delete the record we are trying to update.
282 if (aRecordId
.mCreateVersion
!= inRecordId
.mCreateVersion
)
283 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
);
285 #if RECORDVERSIONCHECK
286 // Is the record we that our update is based on current?
287 if (aRecordId
.mRecordVersion
!= inRecordId
.mRecordVersion
)
288 CssmError::throwMe(CSSMERR_DL_STALE_UNIQUE_RECORD
);
291 // Update the actual packed record.
292 auto_ptr
<WriteSection
> aDbRecord(new WriteSection());
293 getMetaRecord().updateRecord(anOldDbRecord
, *aDbRecord
,
294 CssmDbRecordAttributeData::overlay(inAttributes
), inData
, inModifyMode
);
297 // Bump the RecordVersion of this record.
298 RecordId
aNewRecordId(aRecordNumber
, inRecordId
.mCreateVersion
, inRecordId
.mRecordVersion
+ 1);
299 // Store the RecordVersion in the packed aDbRecord.
300 MetaRecord::packRecordId(aNewRecordId
, *aDbRecord
);
302 if (!aReUpdate
&& !mDeletedSet
.insert(aRecordNumber
).second
)
303 CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND
); // Record was already in mDeletedSet
307 // remove the original record from all the indexes
308 MutableIndexMap::iterator it
;
309 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
310 it
->second
->removeRecord(aRecordNumber
);
312 // add the updated record to all the indexes; this will throw if the new record
313 // violates a unique index
314 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++)
315 it
->second
->insertRecord(aRecordNumber
, *(aDbRecord
.get()));
317 mInsertedMap
.insert(InsertedMap::value_type(aRecordNumber
, aDbRecord
.get()));
323 mDeletedSet
.erase(aRecordNumber
);
331 ModifiedTable::nextRecordNumber()
333 // If we still have unused free records in mTable get the next one.
335 return mTable
->popFreeList(mFreeListHead
);
337 // Bump up the mRecordNumberCount so we don't reuse the same one.
338 return mRecordNumberCount
++;
342 ModifiedTable::recordNumberCount() const
344 uint32 anOldMax
= !mTable
? 0 : mTable
->recordNumberCount() - 1;
345 uint32 anInsertedMax
= mInsertedMap
.empty() ? 0 : mInsertedMap
.rbegin()->first
;
347 DeletedSet::reverse_iterator anIt
= mDeletedSet
.rbegin();
348 DeletedSet::reverse_iterator anEnd
= mDeletedSet
.rend();
349 for (; anIt
!= anEnd
; anIt
++)
351 if (*anIt
!= anOldMax
|| anOldMax
<= anInsertedMax
)
356 return max(anOldMax
,anInsertedMax
) + 1;
360 ModifiedTable::getMetaRecord() const
362 return mNewMetaRecord
? *mNewMetaRecord
: mTable
->getMetaRecord();
365 // prepare to modify the table
368 ModifiedTable::modifyTable()
371 createMutableIndexes();
376 // create mutable indexes from the read-only indexes in the underlying table
379 ModifiedTable::createMutableIndexes()
384 Table::ConstIndexMap::const_iterator it
;
385 for (it
= mTable
->mIndexMap
.begin(); it
!= mTable
->mIndexMap
.end(); it
++) {
386 auto_ptr
<DbMutableIndex
> mutableIndex(new DbMutableIndex(*it
->second
));
387 mIndexMap
.insert(MutableIndexMap::value_type(it
->first
, mutableIndex
.get()));
388 mutableIndex
.release();
392 // find, and create if needed, an index with the given id
395 ModifiedTable::findIndex(uint32 indexId
, const MetaRecord
&metaRecord
, bool isUniqueIndex
)
397 MutableIndexMap::iterator it
= mIndexMap
.find(indexId
);
399 if (it
== mIndexMap
.end()) {
400 // create the new index
401 auto_ptr
<DbMutableIndex
> index(new DbMutableIndex(metaRecord
, indexId
, isUniqueIndex
));
402 it
= mIndexMap
.insert(MutableIndexMap::value_type(indexId
, index
.get())).first
;
410 ModifiedTable::writeIndexSection(WriteSection
&tableSection
, uint32 offset
)
412 MutableIndexMap::iterator it
;
414 tableSection
.put(Table::OffsetIndexesOffset
, offset
);
416 // leave room for the size, to be written later
417 uint32 indexSectionOffset
= offset
;
420 offset
= tableSection
.put(offset
, mIndexMap
.size());
422 // leave room for the array of offsets to the indexes
423 uint32 indexOffsetOffset
= offset
;
424 offset
+= mIndexMap
.size() * AtomSize
;
427 for (it
= mIndexMap
.begin(); it
!= mIndexMap
.end(); it
++) {
428 indexOffsetOffset
= tableSection
.put(indexOffsetOffset
, offset
);
429 offset
= it
->second
->writeIndex(tableSection
, offset
);
432 // write the total index section size
433 tableSection
.put(indexSectionOffset
, offset
- indexSectionOffset
);
439 ModifiedTable::writeTable(AtomicFile
&inAtomicFile
, uint32 inSectionOffset
)
441 if (mTable
&& !mIsModified
) {
442 // the table has not been modified, so we can just dump the old table
443 // section into the new database
445 const ReadSection
&tableSection
= mTable
->getTableSection();
446 uint32 tableSize
= tableSection
.at(Table::OffsetSize
);
448 inAtomicFile
.write(AtomicFile::FromStart
, inSectionOffset
,
449 tableSection
.range(Range(0, tableSize
)), tableSize
);
451 return inSectionOffset
+ tableSize
;
454 // We should have an old mTable or a mNewMetaRecord but not both.
455 assert(mTable
!= nil
^ mNewMetaRecord
!= nil
);
456 const MetaRecord
&aNewMetaRecord
= getMetaRecord();
458 uint32 aRecordsCount
= 0;
459 uint32 aRecordNumbersCount
= recordNumberCount();
460 uint32 aRecordsOffset
= Table::OffsetRecordNumbers
+ AtomSize
* aRecordNumbersCount
;
461 WriteSection
aTableSection(CssmAllocator::standard(), aRecordsOffset
);
462 aTableSection
.size(aRecordsOffset
);
463 aTableSection
.put(Table::OffsetId
, aNewMetaRecord
.dataRecordType());
464 aTableSection
.put(Table::OffsetRecords
, aRecordsOffset
);
465 aTableSection
.put(Table::OffsetRecordNumbersCount
, aRecordNumbersCount
);
467 uint32 anOffset
= inSectionOffset
+ aRecordsOffset
;
471 // XXX Handle schema changes in the future.
472 assert(mNewMetaRecord
== nil
);
474 // We have a modified old table so copy all non deleted records
475 // The code below is rather elaborate, but this is because it attempts
476 // to copy large ranges of non deleted records with single calls
477 // to AtomicFile::write()
478 uint32 anOldRecordsCount
= mTable
->getRecordsCount();
479 ReadSection aRecordsSection
= mTable
->getRecordsSection();
480 uint32 aReadOffset
= 0; // Offset of current record
481 uint32 aWriteOffset
= aRecordsOffset
; // Offset for current write record
482 uint32 aBlockStart
= aReadOffset
; // Starting point for read
483 uint32 aBlockSize
= 0; // Size of block to read
484 for (uint32 aRecord
= 0; aRecord
< anOldRecordsCount
; aRecord
++)
486 ReadSection aRecordSection
= MetaRecord::readSection(aRecordsSection
, aReadOffset
);
487 uint32 aRecordNumber
= MetaRecord::unpackRecordNumber(aRecordSection
);
488 uint32 aRecordSize
= aRecordSection
.size();
489 aReadOffset
+= aRecordSize
;
490 if (mDeletedSet
.find(aRecordNumber
) == mDeletedSet
.end())
492 // This record has not been deleted. Register the offset
493 // at which it will be in the new file in aTableSection.
494 aTableSection
.put(Table::OffsetRecordNumbers
495 + AtomSize
* aRecordNumber
,
497 aWriteOffset
+= aRecordSize
;
498 aBlockSize
+= aRecordSize
;
500 // XXX update all indexes being created.
504 // The current record has been deleted. Copy all records up
505 // to but not including the current one to the new file.
508 inAtomicFile
.write(AtomicFile::FromStart
, anOffset
,
509 aRecordsSection
.range(Range(aBlockStart
,
512 anOffset
+= aBlockSize
;
515 // Set the start of the next block to the start of the next
516 // record, and the size of the block to 0.
517 aBlockStart
= aReadOffset
;
519 } // if (mDeletedSet..)
520 } // for (aRecord...)
522 // Copy all records that have not yet been copied to the new file.
525 inAtomicFile
.write(AtomicFile::FromStart
, anOffset
,
526 aRecordsSection
.range(Range(aBlockStart
,
529 anOffset
+= aBlockSize
;
533 // Now add all inserted records to the table.
534 InsertedMap::const_iterator anIt
= mInsertedMap
.begin();
535 InsertedMap::const_iterator anEnd
= mInsertedMap
.end();
536 // Iterate over all inserted objects.
537 for (; anIt
!= anEnd
; anIt
++)
539 // Write out each inserted/modified record
540 const WriteSection
&aRecord
= *anIt
->second
;
541 uint32 aRecordNumber
= anIt
->first
;
542 // Put offset relative to start of this table in recordNumber array.
543 aTableSection
.put(Table::OffsetRecordNumbers
+ AtomSize
* aRecordNumber
,
544 anOffset
- inSectionOffset
);
545 inAtomicFile
.write(AtomicFile::FromStart
, anOffset
,
546 aRecord
.address(), aRecord
.size());
547 anOffset
+= aRecord
.size();
549 // XXX update all indexes being created.
552 // Reconstruct the freelist (this is O(N) where N is the number of recordNumbers)
553 // We could implement it faster by using the old freelist and skipping the records
554 // that have been inserted. However building the freelist for the newly used
555 // recordNumbers (not in mTable) would look like the code below anyway (starting
556 // from mTable->recordNumberCount()).
557 // The first part of this would be O(M Log(N)) (where M is the old number of
558 // free records, and N is the number of newly inserted records)
559 // The second part would be O(N) where N is the currently max RecordNumber
560 // in use - the old max RecordNumber in use.
561 uint32 aFreeListHead
= 0; // Link to previous free record
562 for (uint32 aRecordNumber
= 0; aRecordNumber
< aRecordNumbersCount
; aRecordNumber
++)
564 // Make the freelist a list of all records with 0 offset (non existing).
565 if (!aTableSection
.at(Table::OffsetRecordNumbers
+ AtomSize
* aRecordNumber
))
567 aTableSection
.put(Table::OffsetRecordNumbers
568 + AtomSize
* aRecordNumber
,
570 // Make aFreeListHead point to the previous free recordNumber slot in the table.
571 aFreeListHead
= (Table::OffsetRecordNumbers
+ AtomSize
* aRecordNumber
) | 1;
574 aTableSection
.put(Table::OffsetFreeListHead
, aFreeListHead
);
576 anOffset
-= inSectionOffset
;
578 // Write out indexes, which are part of the table section
581 uint32 indexOffset
= anOffset
;
582 anOffset
= writeIndexSection(aTableSection
, anOffset
);
583 inAtomicFile
.write(AtomicFile::FromStart
, inSectionOffset
+ indexOffset
,
584 aTableSection
.address() + indexOffset
, anOffset
- indexOffset
);
587 // Set the section size and recordCount.
588 aTableSection
.put(Table::OffsetSize
, anOffset
);
589 aTableSection
.put(Table::OffsetRecordsCount
, aRecordsCount
);
591 // Write out aTableSection header.
592 inAtomicFile
.write(AtomicFile::FromStart
, inSectionOffset
,
593 aTableSection
.address(), aTableSection
.size());
595 return anOffset
+ inSectionOffset
;
604 // Attribute definitions
606 static const CSSM_DB_ATTRIBUTE_INFO RelationID
=
608 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
610 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
612 static const CSSM_DB_ATTRIBUTE_INFO RelationName
=
614 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
616 CSSM_DB_ATTRIBUTE_FORMAT_STRING
618 static const CSSM_DB_ATTRIBUTE_INFO AttributeID
=
620 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
622 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
624 static const CSSM_DB_ATTRIBUTE_INFO AttributeNameFormat
=
626 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
627 {"AttributeNameFormat"},
628 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
630 static const CSSM_DB_ATTRIBUTE_INFO AttributeName
=
632 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
634 CSSM_DB_ATTRIBUTE_FORMAT_STRING
636 static const CSSM_DB_ATTRIBUTE_INFO AttributeNameID
=
638 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
640 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
642 static const CSSM_DB_ATTRIBUTE_INFO AttributeFormat
=
644 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
646 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
648 static const CSSM_DB_ATTRIBUTE_INFO IndexID
=
650 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
652 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
654 static const CSSM_DB_ATTRIBUTE_INFO IndexType
=
656 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
658 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
660 static const CSSM_DB_ATTRIBUTE_INFO IndexedDataLocation
=
662 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
663 {"IndexedDataLocation"},
664 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
666 static const CSSM_DB_ATTRIBUTE_INFO ModuleID
=
668 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
670 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
672 static const CSSM_DB_ATTRIBUTE_INFO AddinVersion
=
674 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
676 CSSM_DB_ATTRIBUTE_FORMAT_STRING
678 static const CSSM_DB_ATTRIBUTE_INFO SSID
=
680 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
682 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
684 static const CSSM_DB_ATTRIBUTE_INFO SubserviceType
=
686 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
688 CSSM_DB_ATTRIBUTE_FORMAT_UINT32
691 #define ATTRIBUTE(type, name) \
692 { CSSM_DB_ATTRIBUTE_NAME_AS_STRING, { #name }, CSSM_DB_ATTRIBUTE_FORMAT_ ## type }
694 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaRelations
[] =
696 //RelationID, RelationName
697 ATTRIBUTE(UINT32
, RelationID
),
698 ATTRIBUTE(STRING
, RelationName
)
701 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaAttributes
[] =
703 //RelationID, AttributeID,
704 //AttributeNameFormat, AttributeName, AttributeNameID,
706 ATTRIBUTE(UINT32
, RelationID
),
707 ATTRIBUTE(UINT32
, AttributeID
),
708 ATTRIBUTE(UINT32
, AttributeNameFormat
),
709 ATTRIBUTE(STRING
, AttributeName
),
710 ATTRIBUTE(BLOB
, AttributeNameID
),
711 ATTRIBUTE(UINT32
, AttributeFormat
)
714 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaIndexes
[] =
716 ATTRIBUTE(UINT32
, RelationID
),
717 ATTRIBUTE(UINT32
, IndexID
),
718 ATTRIBUTE(UINT32
, AttributeID
),
719 ATTRIBUTE(UINT32
, IndexType
),
720 ATTRIBUTE(UINT32
, IndexedDataLocation
)
721 //RelationID, IndexID, AttributeID,
722 //IndexType, IndexedDataLocation
725 static const CSSM_DB_ATTRIBUTE_INFO AttrSchemaParsingModule
[] =
727 ATTRIBUTE(UINT32
, RelationID
),
728 ATTRIBUTE(UINT32
, AttributeID
),
729 ATTRIBUTE(BLOB
, ModuleID
),
730 ATTRIBUTE(STRING
, AddinVersion
),
731 ATTRIBUTE(UINT32
, SSID
),
732 ATTRIBUTE(UINT32
, SubserviceType
)
733 //RelationID, AttributeID,
734 //ModuleID, AddinVersion, SSID, SubserviceType
742 DbVersion::DbVersion(AtomicFile
&inDatabaseFile
,
743 const AppleDatabase
&db
) :
744 mDatabase(reinterpret_cast<const uint8
*>(NULL
), 0), mDatabaseFile(&inDatabaseFile
),
747 const uint8
*aFileAddress
;
749 mVersionId
= mDatabaseFile
->enterRead(aFileAddress
, aLength
);
750 mDatabase
= ReadSection(aFileAddress
, aLength
);
754 DbVersion::~DbVersion()
758 for_each_map_delete(mTableMap
.begin(), mTableMap
.end());
760 mDatabaseFile
->exitRead(mVersionId
);
766 DbVersion::isDirty() const
769 return mDatabaseFile
->isDirty(mVersionId
);
779 // This is the oposite of DbModifier::commit()
780 const ReadSection aHeaderSection
= mDatabase
.subsection(HeaderOffset
,
782 if (aHeaderSection
.at(OffsetMagic
) != HeaderMagic
)
783 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
785 // We currently only support one version. If we support additional
786 // file format versions in the future fix this.
787 uint32 aVersion
= aHeaderSection
.at(OffsetVersion
);
788 if (aVersion
!= HeaderVersion
)
789 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
791 //const ReadSection anAuthSection =
792 // mDatabase.subsection(HeaderOffset + aHeaderSection.at(OffsetAuthOffset));
793 // XXX Do something with anAuthSection.
795 uint32 aSchemaOffset
= aHeaderSection
.at(OffsetSchemaOffset
);
796 const ReadSection aSchemaSection
=
797 mDatabase
.subsection(HeaderOffset
+ aSchemaOffset
);
799 uint32 aSchemaSize
= aSchemaSection
[OffsetSchemaSize
];
800 // Make sure that the given range exists.
801 aSchemaSection
.subsection(0, aSchemaSize
);
802 uint32 aTableCount
= aSchemaSection
[OffsetTablesCount
];
804 // Assert that the size of this section is big enough.
805 if (aSchemaSize
< OffsetTables
+ AtomSize
* aTableCount
)
806 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
808 for (uint32 aTableNumber
= 0; aTableNumber
< aTableCount
;
811 uint32 aTableOffset
= aSchemaSection
.at(OffsetTables
+ AtomSize
813 // XXX Set the size boundary on aTableSection.
814 const ReadSection aTableSection
=
815 aSchemaSection
.subsection(aTableOffset
);
816 auto_ptr
<Table
> aTable(new Table(aTableSection
));
817 Table::Id aTableId
= aTable
->getMetaRecord().dataRecordType();
818 mTableMap
.insert(TableMap::value_type(aTableId
, aTable
.get()));
822 // Fill in the schema for the meta tables.
824 findTable(mDb
.schemaRelations
.DataRecordType
).getMetaRecord().
825 setRecordAttributeInfo(mDb
.schemaRelations
);
826 findTable(mDb
.schemaIndexes
.DataRecordType
).getMetaRecord().
827 setRecordAttributeInfo(mDb
.schemaIndexes
);
828 findTable(mDb
.schemaParsingModule
.DataRecordType
).getMetaRecord().
829 setRecordAttributeInfo(mDb
.schemaParsingModule
);
831 // OK, we have created all the tables in the tableMap. Now
832 // lets read the schema and proccess it accordingly.
833 // Iterate over all schema records.
834 Table
&aTable
= findTable(mDb
.schemaAttributes
.DataRecordType
);
835 aTable
.getMetaRecord().setRecordAttributeInfo(mDb
.schemaAttributes
);
836 uint32 aRecordsCount
= aTable
.getRecordsCount();
837 ReadSection aRecordsSection
= aTable
.getRecordsSection();
838 uint32 aReadOffset
= 0;
839 const MetaRecord
&aMetaRecord
= aTable
.getMetaRecord();
841 CSSM_DB_ATTRIBUTE_DATA aRelationIDData
=
847 CSSM_DB_ATTRIBUTE_DATA aAttributeIDData
=
853 CSSM_DB_ATTRIBUTE_DATA aAttributeNameFormatData
=
859 CSSM_DB_ATTRIBUTE_DATA aAttributeNameData
=
865 CSSM_DB_ATTRIBUTE_DATA aAttributeNameIDData
=
871 CSSM_DB_ATTRIBUTE_DATA aAttributeFormatData
=
877 CSSM_DB_ATTRIBUTE_DATA aRecordAttributes
[] =
881 aAttributeNameFormatData
,
883 aAttributeNameIDData
,
886 CSSM_DB_RECORD_ATTRIBUTE_DATA aRecordAttributeData
=
888 aMetaRecord
.dataRecordType(),
890 sizeof(aRecordAttributes
) / sizeof(CSSM_DB_ATTRIBUTE_DATA
),
893 CssmDbRecordAttributeData
&aRecordData
= CssmDbRecordAttributeData::overlay(aRecordAttributeData
);
895 TrackingAllocator
recordAllocator(CssmAllocator::standard());
896 for (uint32 aRecord
= 0; aRecord
!= aRecordsCount
; aRecord
++)
898 ReadSection aRecordSection
= MetaRecord::readSection(aRecordsSection
, aReadOffset
);
899 uint32 aRecordSize
= aRecordSection
.size();
900 aReadOffset
+= aRecordSize
;
905 aMetaRecord
.unpackRecord(aRecordSection
, recordAllocator
,
906 &aRecordAttributeData
, NULL
, 0);
907 // Create the attribute coresponding to this entry
908 if (aRecordData
[0].size() != 1 || aRecordData
[0].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
)
909 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
910 uint32 aRelationId
= aRecordData
[0];
912 // Skip the schema relations for the meta tables themselves.
913 // FIXME: this hard-wires the meta-table relation IDs to be
914 // within {CSSM_DB_RECORDTYPE_SCHEMA_START...
915 // CSSM_DB_RECORDTYPE_SCHEMA_END} (which is {0..4}).
916 // Bogus - the MDS schema relation IDs start at
917 // CSSM_DB_RELATIONID_MDS_START which is 0x40000000.
918 // Ref. Radar 2817921.
919 if (CSSM_DB_RECORDTYPE_SCHEMA_START
<= aRelationId
&& aRelationId
< CSSM_DB_RECORDTYPE_SCHEMA_END
)
922 // Get the MetaRecord corresponding to the specified RelationId
923 MetaRecord
&aMetaRecord
= findTable(aRelationId
).getMetaRecord();
925 if (aRecordData
[1].size() != 1
926 || aRecordData
[1].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
927 || aRecordData
[2].size() != 1
928 || aRecordData
[2].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
929 || aRecordData
[5].size() != 1
930 || aRecordData
[5].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
)
931 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
933 uint32 anAttributeId
= aRecordData
[1];
934 uint32 anAttributeNameFormat
= aRecordData
[2];
935 uint32 anAttributeFormat
= aRecordData
[5];
936 auto_ptr
<string
> aName
;
937 const CssmData
*aNameID
= NULL
;
939 if (aRecordData
[3].size() == 1)
941 if (aRecordData
[3].format() != CSSM_DB_ATTRIBUTE_FORMAT_STRING
)
942 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
944 auto_ptr
<string
> aName2(new string(static_cast<string
>(aRecordData
[3])));
948 if (aRecordData
[4].size() == 1)
950 if (aRecordData
[4].format() != CSSM_DB_ATTRIBUTE_FORMAT_BLOB
)
951 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
953 // @@@ Invoking conversion operator to CssmData & on aRecordData[4]
954 // And taking address of result.
955 aNameID
= &static_cast<CssmData
&>(aRecordData
[4]);
958 // Make sure that the attribute specified by anAttributeNameFormat is present.
959 switch (anAttributeNameFormat
)
961 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING
:
962 if (aRecordData
[3].size() != 1)
963 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
965 case CSSM_DB_ATTRIBUTE_NAME_AS_OID
:
966 if (aRecordData
[4].size() != 1)
967 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
969 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
:
972 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
975 // Create the attribute
976 aMetaRecord
.createAttribute(aName
.get(), aNameID
, anAttributeId
, anAttributeFormat
);
980 aRecordData
.deleteValues(CssmAllocator::standard());
984 aRecordData
.deleteValues(CssmAllocator::standard());
990 // initialize the indexes associated with each table
992 TableMap::iterator it
;
993 for (it
= mTableMap
.begin(); it
!= mTableMap
.end(); it
++)
994 it
->second
->readIndexSection();
999 for_each_map_delete(mTableMap
.begin(), mTableMap
.end());
1006 DbVersion::getRecord(Table::Id inTableId
, const RecordId
&inRecordId
,
1007 CSSM_DB_RECORD_ATTRIBUTE_DATA
*inoutAttributes
,
1008 CssmData
*inoutData
,
1009 CssmAllocator
&inAllocator
) const
1011 return findTable(inTableId
).getRecord(inRecordId
, inoutAttributes
,
1012 inoutData
, inAllocator
);
1016 DbVersion::createCursor(const CSSM_QUERY
*inQuery
) const
1018 // XXX We should add support for these special query types
1019 // By Creating a Cursor that iterates over multiple tables
1020 if (!inQuery
|| inQuery
->RecordType
== CSSM_DL_DB_RECORD_ANY
1021 || inQuery
->RecordType
== CSSM_DL_DB_RECORD_ALL_KEYS
)
1023 return new MultiCursor(inQuery
, *this);
1026 return findTable(inQuery
->RecordType
).createCursor(inQuery
, *this);
1030 DbVersion::findTable(Table::Id inTableId
) const
1032 TableMap::const_iterator it
= mTableMap
.find(inTableId
);
1033 if (it
== mTableMap
.end())
1034 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1039 DbVersion::findTable(Table::Id inTableId
)
1041 TableMap::iterator it
= mTableMap
.find(inTableId
);
1042 if (it
== mTableMap
.end())
1043 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1048 // Cursor implemetation
1056 // LinearCursor implemetation
1058 LinearCursor::LinearCursor(const CSSM_QUERY
*inQuery
, const DbVersion
&inDbVersion
,
1059 const Table
&inTable
) :
1060 mDbVersion(&inDbVersion
),
1061 mRecordsCount(inTable
.getRecordsCount()),
1063 mRecordsSection(inTable
.getRecordsSection()),
1065 mMetaRecord(inTable
.getMetaRecord())
1069 mConjunctive
= inQuery
->Conjunctive
;
1070 mQueryFlags
= inQuery
->QueryFlags
;
1071 // XXX Do something with inQuery->QueryLimits?
1072 uint32 aPredicatesCount
= inQuery
->NumSelectionPredicates
;
1073 mPredicates
.resize(aPredicatesCount
);
1076 for (uint32 anIndex
= 0; anIndex
< aPredicatesCount
; anIndex
++)
1078 CSSM_SELECTION_PREDICATE
&aPredicate
= inQuery
->SelectionPredicate
[anIndex
];
1079 mPredicates
[anIndex
] = new SelectionPredicate(mMetaRecord
, aPredicate
);
1084 for_each_delete(mPredicates
.begin(), mPredicates
.end());
1090 LinearCursor::~LinearCursor()
1092 for_each_delete(mPredicates
.begin(), mPredicates
.end());
1096 LinearCursor::next(Table::Id
&outTableId
,
1097 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
1098 CssmData
*inoutData
, CssmAllocator
&inAllocator
, RecordId
&recordId
)
1100 while (mRecord
++ < mRecordsCount
)
1102 ReadSection aRecordSection
= MetaRecord::readSection(mRecordsSection
, mReadOffset
);
1103 uint32 aRecordSize
= aRecordSection
.size();
1104 mReadOffset
+= aRecordSize
;
1106 PredicateVector::const_iterator anIt
= mPredicates
.begin();
1107 PredicateVector::const_iterator anEnd
= mPredicates
.end();
1111 // If there are no predicates we have a match.
1114 else if (mConjunctive
== CSSM_DB_OR
)
1116 // If mConjunctive is OR, the first predicate that returns
1117 // true indicates a match. Dropthough means no match
1119 for (; anIt
!= anEnd
; anIt
++)
1121 if ((*anIt
)->evaluate(aRecordSection
))
1128 else if (mConjunctive
== CSSM_DB_AND
|| mConjunctive
== CSSM_DB_NONE
)
1130 // If mConjunctive is AND (or NONE), the first predicate that returns
1131 // false indicates a mismatch. Dropthough means a match
1133 for (; anIt
!= anEnd
; anIt
++)
1135 if (!(*anIt
)->evaluate(aRecordSection
))
1144 // XXX Should be CSSMERR_DL_INVALID_QUERY (or CSSMERR_DL_INVALID_CONJUNTIVE).
1145 CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_QUERY
);
1150 // Get the actual record.
1151 mMetaRecord
.unpackRecord(aRecordSection
, inAllocator
,
1152 inoutAttributes
, inoutData
,
1154 outTableId
= mMetaRecord
.dataRecordType();
1155 recordId
= MetaRecord::unpackRecordId(aRecordSection
);
1167 IndexCursor::IndexCursor(DbQueryKey
*queryKey
, const DbVersion
&inDbVersion
,
1168 const Table
&table
, const DbConstIndex
*index
)
1169 : mQueryKey(queryKey
), mDbVersion(inDbVersion
), mTable(table
), mIndex(index
)
1171 index
->performQuery(*queryKey
, mBegin
, mEnd
);
1174 IndexCursor::~IndexCursor()
1176 // the query key will be deleted automatically, since it's an auto_ptr
1180 IndexCursor::next(Table::Id
&outTableId
,
1181 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes
,
1183 CssmAllocator
&inAllocator
, RecordId
&recordId
)
1188 ReadSection rs
= mIndex
->getRecordSection(mBegin
++);
1189 const MetaRecord
&metaRecord
= mTable
.getMetaRecord();
1191 outTableId
= metaRecord
.dataRecordType();
1192 metaRecord
.unpackRecord(rs
, inAllocator
, outAttributes
, outData
, 0);
1194 recordId
= MetaRecord::unpackRecordId(rs
);
1201 MultiCursor::MultiCursor(const CSSM_QUERY
*inQuery
, const DbVersion
&inDbVersion
) :
1202 mDbVersion(&inDbVersion
), mTableIterator(inDbVersion
.begin())
1205 mQuery
.reset(new CssmAutoQuery(*inQuery
));
1208 mQuery
.reset(new CssmAutoQuery());
1209 mQuery
->recordType(CSSM_DL_DB_RECORD_ANY
);
1213 MultiCursor::~MultiCursor()
1218 MultiCursor::next(Table::Id
&outTableId
,
1219 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
1220 CssmData
*inoutData
, CssmAllocator
&inAllocator
, RecordId
&recordId
)
1226 if (mTableIterator
== mDbVersion
->end())
1229 const Table
&aTable
= *mTableIterator
++;
1230 if (!aTable
.matchesTableId(mQuery
->recordType()))
1233 mCursor
.reset(aTable
.createCursor(mQuery
.get(), *mDbVersion
));
1236 if (mCursor
->next(outTableId
, inoutAttributes
, inoutData
, inAllocator
, recordId
))
1239 mCursor
.reset(NULL
);
1247 DbModifier::DbModifier(AtomicFile
&inAtomicFile
, const AppleDatabase
&db
) :
1250 mAtomicFile(inAtomicFile
),
1256 DbModifier::~DbModifier()
1260 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1268 const RefPointer
<const DbVersion
>
1269 DbModifier::getDbVersion()
1271 StLock
<Mutex
> _(mDbVersionLock
);
1272 if (mDbVersion
&& mDbVersion
->isDirty())
1275 if (mDbVersion
== NULL
)
1276 mDbVersion
= new DbVersion(mAtomicFile
, mDb
);
1282 DbModifier::createDatabase(const CSSM_DBINFO
&inDbInfo
,
1283 const CSSM_ACL_ENTRY_INPUT
*inInitialAclEntry
)
1285 // XXX This needs better locking. There is a possible race condition between
1286 // two concurrent creators. Or a writer/creator or a close/create etc.
1287 if (mWriting
|| !mModifiedTableMap
.empty())
1288 CssmError::throwMe(CSSMERR_DL_DATASTORE_ALREADY_EXISTS
);
1290 mVersionId
= mAtomicFile
.enterCreate(mFileRef
);
1293 // we need to create the meta tables first, because inserting tables
1294 // (including the meta tables themselves) relies on them being there
1295 createTable(new MetaRecord(mDb
.schemaRelations
));
1296 createTable(new MetaRecord(mDb
.schemaAttributes
));
1297 createTable(new MetaRecord(mDb
.schemaIndexes
));
1298 createTable(new MetaRecord(mDb
.schemaParsingModule
));
1300 // now add the meta-tables' schema to the meta tables themselves
1301 insertTableSchema(mDb
.schemaRelations
);
1302 insertTableSchema(mDb
.schemaAttributes
);
1303 insertTableSchema(mDb
.schemaIndexes
);
1304 insertTableSchema(mDb
.schemaParsingModule
);
1306 if (inInitialAclEntry
!= NULL
)
1308 //createACL(*inInitialAclEntry);
1311 if (inDbInfo
.NumberOfRecordTypes
== 0)
1313 if (inDbInfo
.RecordAttributeNames
== NULL
)
1314 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1315 if (inDbInfo
.RecordIndexes
== NULL
)
1316 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_INDEX
);
1317 if (inDbInfo
.DefaultParsingModules
== NULL
)
1318 CssmError::throwMe(CSSMERR_DL_INVALID_PARSING_MODULE
);
1320 for (uint32 anIndex
= 0; anIndex
< inDbInfo
.NumberOfRecordTypes
; anIndex
++)
1322 insertTable(CssmDbRecordAttributeInfo::overlay(inDbInfo
.RecordAttributeNames
[anIndex
]),
1323 &inDbInfo
.RecordIndexes
[anIndex
],
1324 &inDbInfo
.DefaultParsingModules
[anIndex
]);
1328 void DbModifier::openDatabase()
1330 commit(); // XXX Requires write lock.
1334 void DbModifier::closeDatabase()
1336 commit(); // XXX Requires write lock.
1337 StLock
<Mutex
> _(mDbVersionLock
);
1341 void DbModifier::deleteDatabase()
1343 rollback(); // XXX Requires write lock. Also if autoCommit was disabled
1344 // this will incorrectly cause the performDelete to throw CSSMERR_DB_DOES_NOT_EXIST.
1345 StLock
<Mutex
> _(mDbVersionLock
);
1347 // Clean up mModifiedTableMap in case this object gets reused again for
1349 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1350 mModifiedTableMap
.clear();
1353 mAtomicFile
.performDelete();
1357 DbModifier::modifyDatabase()
1364 const uint8
*aFileAddress
;
1366 mVersionId
= mAtomicFile
.enterWrite(aFileAddress
, aLength
, mFileRef
);
1369 // Aquire the mutex protecting mDbVersion
1370 StLock
<Mutex
> _l(mDbVersionLock
);
1371 if (mDbVersion
== nil
|| mDbVersion
->getVersionId() != mVersionId
)
1373 // This will call enterRead(). Now that we hold the write
1374 // lock on the file this ensures we get the same verison
1375 // enterWrite just returned.
1376 mDbVersion
= new DbVersion(mAtomicFile
, mDb
);
1380 // Remove all old modified tables
1381 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1382 mModifiedTableMap
.clear();
1384 // Setup the new tables
1385 DbVersion::TableMap::const_iterator anIt
=
1386 mDbVersion
->mTableMap
.begin();
1387 DbVersion::TableMap::const_iterator anEnd
=
1388 mDbVersion
->mTableMap
.end();
1389 for (; anIt
!= anEnd
; ++anIt
)
1391 auto_ptr
<ModifiedTable
> aTable(new ModifiedTable(anIt
->second
));
1392 mModifiedTableMap
.insert(ModifiedTableMap::value_type(anIt
->first
,
1399 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1400 mModifiedTableMap
.clear();
1407 DbModifier::deleteRecord(Table::Id inTableId
, const RecordId
&inRecordId
)
1410 findTable(inTableId
).deleteRecord(inRecordId
);
1414 DbModifier::insertRecord(Table::Id inTableId
,
1415 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
1416 const CssmData
*inData
)
1419 return findTable(inTableId
).insertRecord(mVersionId
, inAttributes
, inData
);
1423 DbModifier::updateRecord(Table::Id inTableId
, const RecordId
&inRecordId
,
1424 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
1425 const CssmData
*inData
,
1426 CSSM_DB_MODIFY_MODE inModifyMode
)
1428 commit(); // XXX this is not thread safe, but what is?
1430 return findTable(inTableId
).updateRecord(inRecordId
, inAttributes
, inData
, inModifyMode
);
1433 // Create a table associated with a given metarecord, and add the table
1437 DbModifier::createTable(MetaRecord
*inMetaRecord
)
1439 auto_ptr
<MetaRecord
> aMetaRecord(inMetaRecord
);
1440 auto_ptr
<ModifiedTable
> aModifiedTable(new ModifiedTable(inMetaRecord
));
1441 // Now that aModifiedTable is fully constructed it owns inMetaRecord
1442 aMetaRecord
.release();
1444 if (!mModifiedTableMap
.insert
1445 (ModifiedTableMap::value_type(inMetaRecord
->dataRecordType(),
1446 aModifiedTable
.get())).second
)
1448 // XXX Should be CSSMERR_DL_DUPLICATE_RECORDTYPE. Since that
1449 // doesn't exist we report that the metatable's unique index would
1450 // no longer be valid
1451 CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
);
1454 return aModifiedTable
.release();
1458 DbModifier::deleteTable(Table::Id inTableId
)
1461 // Can't delete schema tables.
1462 if (CSSM_DB_RECORDTYPE_SCHEMA_START
<= inTableId
1463 && inTableId
< CSSM_DB_RECORDTYPE_SCHEMA_END
)
1464 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1466 // Find the ModifiedTable and delete it
1467 ModifiedTableMap::iterator it
= mModifiedTableMap
.find(inTableId
);
1468 if (it
== mModifiedTableMap
.end())
1469 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1472 mModifiedTableMap
.erase(it
);
1476 DbModifier::writeAuthSection(uint32 inSectionOffset
)
1478 WriteSection anAuthSection
;
1480 // XXX Put real data into the authsection.
1481 uint32 anOffset
= anAuthSection
.put(0, 0);
1482 anAuthSection
.size(anOffset
);
1484 mAtomicFile
.write(AtomicFile::FromStart
, inSectionOffset
,
1485 anAuthSection
.address(), anAuthSection
.size());
1486 return inSectionOffset
+ anOffset
;
1490 DbModifier::writeSchemaSection(uint32 inSectionOffset
)
1492 uint32 aTableCount
= mModifiedTableMap
.size();
1493 WriteSection
aTableSection(CssmAllocator::standard(),
1494 OffsetTables
+ AtomSize
* aTableCount
);
1495 // Set aTableSection to the correct size.
1496 aTableSection
.size(OffsetTables
+ AtomSize
* aTableCount
);
1497 aTableSection
.put(OffsetTablesCount
, aTableCount
);
1499 uint32 anOffset
= inSectionOffset
+ OffsetTables
+ AtomSize
* aTableCount
;
1500 ModifiedTableMap::const_iterator anIt
= mModifiedTableMap
.begin();
1501 ModifiedTableMap::const_iterator anEnd
= mModifiedTableMap
.end();
1502 for (uint32 aTableNumber
= 0; anIt
!= anEnd
; anIt
++, aTableNumber
++)
1504 // Put the offset to the current table relative to the start of
1505 // this section into the tables array
1506 aTableSection
.put(OffsetTables
+ AtomSize
* aTableNumber
,
1507 anOffset
- inSectionOffset
);
1508 anOffset
= anIt
->second
->writeTable(mAtomicFile
, anOffset
);
1511 aTableSection
.put(OffsetSchemaSize
, anOffset
- inSectionOffset
);
1512 mAtomicFile
.write(AtomicFile::FromStart
, inSectionOffset
,
1513 aTableSection
.address(), aTableSection
.size());
1519 DbModifier::commit()
1525 WriteSection
aHeaderSection(CssmAllocator::standard(), size_t(HeaderSize
));
1526 // Set aHeaderSection to the correct size.
1527 aHeaderSection
.size(HeaderSize
);
1529 // Start writing sections after the header
1530 uint32 anOffset
= HeaderOffset
+ HeaderSize
;
1532 // Write auth section
1533 aHeaderSection
.put(OffsetAuthOffset
, anOffset
);
1534 anOffset
= writeAuthSection(anOffset
);
1535 // Write schema section
1536 aHeaderSection
.put(OffsetSchemaOffset
, anOffset
);
1537 anOffset
= writeSchemaSection(anOffset
);
1539 // Write out the file header.
1540 aHeaderSection
.put(OffsetMagic
, HeaderMagic
);
1541 aHeaderSection
.put(OffsetVersion
, HeaderVersion
);
1542 mAtomicFile
.write(AtomicFile::FromStart
, HeaderOffset
,
1543 aHeaderSection
.address(), aHeaderSection
.size());
1549 rollback(); // Sets mWriting to false;
1556 mAtomicFile
.commit();
1560 DbModifier::rollback()
1565 mAtomicFile
.rollback();
1570 DbModifier::getRecord(Table::Id inTableId
, const RecordId
&inRecordId
,
1571 CSSM_DB_RECORD_ATTRIBUTE_DATA
*inoutAttributes
,
1572 CssmData
*inoutData
, CssmAllocator
&inAllocator
)
1574 // XXX never call commit(), rather search our own record tables.
1575 commit(); // XXX Requires write lock.
1576 return getDbVersion()->getRecord(inTableId
, inRecordId
,
1577 inoutAttributes
, inoutData
, inAllocator
);
1581 DbModifier::createCursor(const CSSM_QUERY
*inQuery
)
1583 // XXX Be smarter as to when we must call commit (i.e. don't
1584 // force commit if the table being queried has not been modified).
1585 commit(); // XXX Requires write lock.
1586 return getDbVersion()->createCursor(inQuery
);
1589 // Insert schema records for a new table into the metatables of the database. This gets
1590 // called while a database is being created.
1593 DbModifier::insertTableSchema(const CssmDbRecordAttributeInfo
&inInfo
,
1594 const CSSM_DB_RECORD_INDEX_INFO
*inIndexInfo
/* = NULL */)
1596 ModifiedTable
&aTable
= findTable(inInfo
.DataRecordType
);
1597 const MetaRecord
&aMetaRecord
= aTable
.getMetaRecord();
1599 CssmAutoDbRecordAttributeData
aRecordBuilder(5); // Set capacity to 5 so we don't need to grow
1601 // Create the entry for the SchemaRelations table.
1602 aRecordBuilder
.add(RelationID
, inInfo
.recordType());
1603 aRecordBuilder
.add(RelationName
, mDb
.recordName(inInfo
.recordType()));
1605 // Insert the record into the SchemaRelations ModifiedTable
1606 findTable(mDb
.schemaRelations
.DataRecordType
).insertRecord(mVersionId
,
1607 &aRecordBuilder
, NULL
);
1609 ModifiedTable
&anAttributeTable
= findTable(mDb
.schemaAttributes
.DataRecordType
);
1610 for (uint32 anIndex
= 0; anIndex
< inInfo
.size(); anIndex
++)
1612 // Create an entry for the SchemaAttributes table.
1613 aRecordBuilder
.clear();
1614 aRecordBuilder
.add(RelationID
, inInfo
.recordType());
1615 aRecordBuilder
.add(AttributeNameFormat
, inInfo
.at(anIndex
).nameFormat());
1617 uint32 attributeId
= aMetaRecord
.metaAttribute(inInfo
.at(anIndex
)).attributeId();
1619 switch (inInfo
.at(anIndex
).nameFormat())
1621 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING
:
1622 aRecordBuilder
.add(AttributeName
, inInfo
.at(anIndex
).Label
.AttributeName
);
1624 case CSSM_DB_ATTRIBUTE_NAME_AS_OID
:
1625 aRecordBuilder
.add(AttributeNameID
, inInfo
.at(anIndex
).Label
.AttributeOID
);
1627 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
:
1630 CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME
);
1633 aRecordBuilder
.add(AttributeID
, attributeId
);
1634 aRecordBuilder
.add(AttributeFormat
, inInfo
.at(anIndex
).format());
1636 // Insert the record into the SchemaAttributes ModifiedTable
1637 anAttributeTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1640 if (inIndexInfo
!= NULL
) {
1642 if (inIndexInfo
->DataRecordType
!= inInfo
.DataRecordType
&&
1643 inIndexInfo
->NumberOfIndexes
> 0)
1644 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1646 ModifiedTable
&indexMetaTable
= findTable(mDb
.schemaIndexes
.DataRecordType
);
1647 uint32 aNumberOfIndexes
= inIndexInfo
->NumberOfIndexes
;
1649 for (uint32 anIndex
= 0; anIndex
< aNumberOfIndexes
; anIndex
++)
1651 const CssmDbIndexInfo
&thisIndex
= CssmDbIndexInfo::overlay(inIndexInfo
->IndexInfo
[anIndex
]);
1653 // make sure the index is supported
1654 if (thisIndex
.dataLocation() != CSSM_DB_INDEX_ON_ATTRIBUTE
)
1655 CssmError::throwMe(CSSMERR_DL_INVALID_INDEX_INFO
);
1657 // assign an index ID: the unique index is ID 0, all others are ID > 0
1659 if (thisIndex
.IndexType
== CSSM_DB_INDEX_UNIQUE
)
1662 indexId
= anIndex
+ 1;
1664 // figure out the attribute ID
1665 uint32 attributeId
=
1666 aMetaRecord
.metaAttribute(thisIndex
.Info
).attributeId();
1668 // Create an entry for the SchemaIndexes table.
1669 aRecordBuilder
.clear();
1670 aRecordBuilder
.add(RelationID
, inInfo
.DataRecordType
);
1671 aRecordBuilder
.add(IndexID
, indexId
);
1672 aRecordBuilder
.add(AttributeID
, attributeId
);
1673 aRecordBuilder
.add(IndexType
, thisIndex
.IndexType
);
1674 aRecordBuilder
.add(IndexedDataLocation
, thisIndex
.IndexedDataLocation
);
1676 // Insert the record into the SchemaIndexes ModifiedTable
1677 indexMetaTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1679 // update the table's index objects
1680 DbMutableIndex
&index
= aTable
.findIndex(indexId
, aMetaRecord
, indexId
== 0);
1681 index
.appendAttribute(attributeId
);
1686 // Insert a new table. The attribute info is required; the index and parsing module
1687 // descriptions are optional. This version gets called during the creation of a
1691 DbModifier::insertTable(const CssmDbRecordAttributeInfo
&inInfo
,
1692 const CSSM_DB_RECORD_INDEX_INFO
*inIndexInfo
/* = NULL */,
1693 const CSSM_DB_PARSING_MODULE_INFO
*inParsingModule
/* = NULL */)
1696 createTable(new MetaRecord(inInfo
));
1697 insertTableSchema(inInfo
, inIndexInfo
);
1700 // Insert a new table. This is the version that gets called when a table is added
1701 // after a database has been created.
1704 DbModifier::insertTable(Table::Id inTableId
, const string
&inTableName
,
1705 uint32 inNumberOfAttributes
,
1706 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
*inAttributeInfo
,
1707 uint32 inNumberOfIndexes
,
1708 const CSSM_DB_SCHEMA_INDEX_INFO
*inIndexInfo
)
1711 ModifiedTable
*aTable
= createTable(new MetaRecord(inTableId
, inNumberOfAttributes
, inAttributeInfo
));
1713 CssmAutoDbRecordAttributeData
aRecordBuilder(6); // Set capacity to 6 so we don't need to grow
1715 // Create the entry for the SchemaRelations table.
1716 aRecordBuilder
.add(RelationID
, inTableId
);
1717 aRecordBuilder
.add(RelationName
, inTableName
);
1719 // Insert the record into the SchemaRelations ModifiedTable
1720 findTable(mDb
.schemaRelations
.DataRecordType
).insertRecord(mVersionId
,
1721 &aRecordBuilder
, NULL
);
1723 ModifiedTable
&anAttributeTable
= findTable(mDb
.schemaAttributes
.DataRecordType
);
1724 for (uint32 anIndex
= 0; anIndex
< inNumberOfAttributes
; anIndex
++)
1726 // Create an entry for the SchemaAttributes table.
1727 aRecordBuilder
.clear();
1728 aRecordBuilder
.add(RelationID
, inTableId
);
1729 // XXX What should this be? We set it to CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER for now
1730 // since the AttributeID is always valid.
1731 aRecordBuilder
.add(AttributeNameFormat
, uint32(CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
));
1732 aRecordBuilder
.add(AttributeID
, inAttributeInfo
[anIndex
].AttributeId
);
1733 if (inAttributeInfo
[anIndex
].AttributeName
)
1734 aRecordBuilder
.add(AttributeName
, inAttributeInfo
[anIndex
].AttributeName
);
1735 if (inAttributeInfo
[anIndex
].AttributeNameID
.Length
> 0)
1736 aRecordBuilder
.add(AttributeNameID
, inAttributeInfo
[anIndex
].AttributeNameID
);
1737 aRecordBuilder
.add(AttributeFormat
, inAttributeInfo
[anIndex
].DataType
);
1739 // Insert the record into the SchemaAttributes ModifiedTable
1740 anAttributeTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1743 ModifiedTable
&anIndexTable
= findTable(mDb
.schemaIndexes
.DataRecordType
);
1744 for (uint32 anIndex
= 0; anIndex
< inNumberOfIndexes
; anIndex
++)
1746 // Create an entry for the SchemaIndexes table.
1747 aRecordBuilder
.clear();
1748 aRecordBuilder
.add(RelationID
, inTableId
);
1749 aRecordBuilder
.add(IndexID
, inIndexInfo
[anIndex
].IndexId
);
1750 aRecordBuilder
.add(AttributeID
, inIndexInfo
[anIndex
].AttributeId
);
1751 aRecordBuilder
.add(IndexType
, inIndexInfo
[anIndex
].IndexType
);
1752 aRecordBuilder
.add(IndexedDataLocation
, inIndexInfo
[anIndex
].IndexedDataLocation
);
1754 // Insert the record into the SchemaIndexes ModifiedTable
1755 anIndexTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1757 // update the table's index objects
1758 DbMutableIndex
&index
= aTable
->findIndex(inIndexInfo
[anIndex
].IndexId
,
1759 aTable
->getMetaRecord(), inIndexInfo
[anIndex
].IndexType
== CSSM_DB_INDEX_UNIQUE
);
1760 index
.appendAttribute(inIndexInfo
[anIndex
].AttributeId
);
1765 DbModifier::findTable(Table::Id inTableId
)
1767 ModifiedTableMap::iterator it
= mModifiedTableMap
.find(inTableId
);
1768 if (it
== mModifiedTableMap
.end())
1769 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1775 // AppleDatabaseManager implementation
1778 AppleDatabaseManager::AppleDatabaseManager(const AppleDatabaseTableName
*tableNames
)
1779 : DatabaseManager(),
1780 mTableNames(tableNames
)
1782 // make sure that a proper set of table ids and names has been provided
1785 CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR
);
1788 for (i
= 0; mTableNames
[i
].mTableName
; i
++) {}
1789 if (i
< AppleDatabaseTableName::kNumRequiredTableNames
)
1790 CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR
);
1795 AppleDatabaseManager::make(const DbName
&inDbName
)
1797 return new AppleDatabase(inDbName
, mTableNames
);
1801 // AppleDbContext implementation
1803 AppleDbContext::AppleDbContext(Database
&inDatabase
,
1804 DatabaseSession
&inDatabaseSession
,
1805 CSSM_DB_ACCESS_TYPE inAccessRequest
,
1806 const AccessCredentials
*inAccessCred
,
1807 const void *inOpenParameters
) :
1808 DbContext(inDatabase
, inDatabaseSession
, inAccessRequest
, inAccessCred
)
1810 const CSSM_APPLEDL_OPEN_PARAMETERS
*anOpenParameters
=
1811 reinterpret_cast<const CSSM_APPLEDL_OPEN_PARAMETERS
*>(inOpenParameters
);
1812 if (anOpenParameters
)
1814 if (anOpenParameters
->length
< sizeof(CSSM_APPLEDL_OPEN_PARAMETERS
)
1815 || anOpenParameters
->version
!= 0)
1816 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS
);
1818 mAutoCommit
= anOpenParameters
->autoCommit
== CSSM_FALSE
? false : true;
1824 AppleDbContext::~AppleDbContext()
1829 // AppleDatabase implementation
1831 AppleDatabase::AppleDatabase(const DbName
&inDbName
, const AppleDatabaseTableName
*tableNames
) :
1833 schemaRelations(tableNames
[AppleDatabaseTableName::kSchemaInfo
].mTableId
,
1834 sizeof(AttrSchemaRelations
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
1835 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaRelations
)),
1836 schemaAttributes(tableNames
[AppleDatabaseTableName::kSchemaAttributes
].mTableId
,
1837 sizeof(AttrSchemaAttributes
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
1838 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaAttributes
)),
1839 schemaIndexes(tableNames
[AppleDatabaseTableName::kSchemaIndexes
].mTableId
,
1840 sizeof(AttrSchemaIndexes
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
1841 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaIndexes
)),
1842 schemaParsingModule(tableNames
[AppleDatabaseTableName::kSchemaParsingModule
].mTableId
,
1843 sizeof(AttrSchemaParsingModule
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
1844 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaParsingModule
)),
1845 mAtomicFile(mDbName
),
1846 mDbModifier(mAtomicFile
, *this),
1847 mTableNames(tableNames
)
1851 AppleDatabase::~AppleDatabase()
1855 // Return the name of a record type. This uses a table that maps record types
1856 // to record names. The table is provided when the database is created.
1858 const char *AppleDatabase::recordName(CSSM_DB_RECORDTYPE inRecordType
) const
1860 if (inRecordType
== CSSM_DL_DB_RECORD_ANY
|| inRecordType
== CSSM_DL_DB_RECORD_ALL_KEYS
)
1861 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1863 for (uint32 i
= 0; mTableNames
[i
].mTableName
; i
++)
1864 if (mTableNames
[i
].mTableId
== inRecordType
)
1865 return mTableNames
[i
].mTableName
;
1871 AppleDatabase::makeDbContext(DatabaseSession
&inDatabaseSession
,
1872 CSSM_DB_ACCESS_TYPE inAccessRequest
,
1873 const AccessCredentials
*inAccessCred
,
1874 const void *inOpenParameters
)
1876 return new AppleDbContext(*this, inDatabaseSession
, inAccessRequest
,
1877 inAccessCred
, inOpenParameters
);
1881 AppleDatabase::dbCreate(DbContext
&inDbContext
, const CSSM_DBINFO
&inDBInfo
,
1882 const CSSM_ACL_ENTRY_INPUT
*inInitialAclEntry
)
1886 StLock
<Mutex
> _(mWriteLock
);
1887 mDbModifier
.createDatabase(inDBInfo
, inInitialAclEntry
);
1891 mDbModifier
.rollback();
1894 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
1895 mDbModifier
.commit();
1899 AppleDatabase::dbOpen(DbContext
&inDbContext
)
1901 mDbModifier
.openDatabase();
1905 AppleDatabase::dbClose()
1907 StLock
<Mutex
> _(mWriteLock
);
1908 mDbModifier
.closeDatabase();
1912 AppleDatabase::dbDelete(DatabaseSession
&inDatabaseSession
,
1913 const AccessCredentials
*inAccessCred
)
1915 StLock
<Mutex
> _(mWriteLock
);
1916 // XXX Check callers credentials.
1917 mDbModifier
.deleteDatabase();
1921 AppleDatabase::createRelation(DbContext
&inDbContext
,
1922 CSSM_DB_RECORDTYPE inRelationID
,
1923 const char *inRelationName
,
1924 uint32 inNumberOfAttributes
,
1925 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
&inAttributeInfo
,
1926 uint32 inNumberOfIndexes
,
1927 const CSSM_DB_SCHEMA_INDEX_INFO
&inIndexInfo
)
1931 StLock
<Mutex
> _(mWriteLock
);
1932 // XXX Fix the refs here.
1933 mDbModifier
.insertTable(inRelationID
, inRelationName
,
1934 inNumberOfAttributes
, &inAttributeInfo
,
1935 inNumberOfIndexes
, &inIndexInfo
);
1939 mDbModifier
.rollback();
1942 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
1943 mDbModifier
.commit();
1947 AppleDatabase::destroyRelation(DbContext
&inDbContext
,
1948 CSSM_DB_RECORDTYPE inRelationID
)
1952 StLock
<Mutex
> _(mWriteLock
);
1953 mDbModifier
.deleteTable(inRelationID
);
1957 mDbModifier
.rollback();
1960 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
1961 mDbModifier
.commit();
1965 AppleDatabase::authenticate(DbContext
&inDbContext
,
1966 CSSM_DB_ACCESS_TYPE inAccessRequest
,
1967 const AccessCredentials
&inAccessCred
)
1969 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
1973 AppleDatabase::getDbAcl(DbContext
&inDbContext
,
1974 const CSSM_STRING
*inSelectionTag
,
1975 uint32
&outNumberOfAclInfos
,
1976 CSSM_ACL_ENTRY_INFO_PTR
&outAclInfos
)
1978 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
1982 AppleDatabase::changeDbAcl(DbContext
&inDbContext
,
1983 const AccessCredentials
&inAccessCred
,
1984 const CSSM_ACL_EDIT
&inAclEdit
)
1986 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
1990 AppleDatabase::getDbOwner(DbContext
&inDbContext
,
1991 CSSM_ACL_OWNER_PROTOTYPE
&outOwner
)
1993 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
1997 AppleDatabase::changeDbOwner(DbContext
&inDbContext
,
1998 const AccessCredentials
&inAccessCred
,
1999 const CSSM_ACL_OWNER_PROTOTYPE
&inNewOwner
)
2001 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2005 AppleDatabase::getDbNameFromHandle(const DbContext
&inDbContext
) const
2007 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2010 CSSM_DB_UNIQUE_RECORD_PTR
2011 AppleDatabase::dataInsert(DbContext
&inDbContext
,
2012 CSSM_DB_RECORDTYPE inRecordType
,
2013 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
2014 const CssmData
*inData
)
2016 CSSM_DB_UNIQUE_RECORD_PTR anUniqueRecordPtr
= NULL
;
2019 StLock
<Mutex
> _(mWriteLock
);
2020 const RecordId aRecordId
=
2021 mDbModifier
.insertRecord(inRecordType
, inAttributes
, inData
);
2023 anUniqueRecordPtr
= createUniqueRecord(inDbContext
, inRecordType
,
2025 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2026 mDbModifier
.commit();
2030 if (anUniqueRecordPtr
!= NULL
)
2031 freeUniqueRecord(inDbContext
, *anUniqueRecordPtr
);
2033 mDbModifier
.rollback();
2037 return anUniqueRecordPtr
;
2041 AppleDatabase::dataDelete(DbContext
&inDbContext
,
2042 const CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
)
2046 StLock
<Mutex
> _(mWriteLock
);
2048 const RecordId
aRecordId(parseUniqueRecord(inUniqueRecord
, aTableId
));
2049 mDbModifier
.deleteRecord(aTableId
, aRecordId
);
2053 mDbModifier
.rollback();
2057 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2058 mDbModifier
.commit();
2062 AppleDatabase::dataModify(DbContext
&inDbContext
,
2063 CSSM_DB_RECORDTYPE inRecordType
,
2064 CSSM_DB_UNIQUE_RECORD
&inoutUniqueRecord
,
2065 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributesToBeModified
,
2066 const CssmData
*inDataToBeModified
,
2067 CSSM_DB_MODIFY_MODE inModifyMode
)
2071 StLock
<Mutex
> _(mWriteLock
);
2073 const RecordId aRecordId
=
2074 mDbModifier
.updateRecord(aTableId
,
2075 parseUniqueRecord(inoutUniqueRecord
, aTableId
),
2076 inAttributesToBeModified
,
2079 updateUniqueRecord(inDbContext
, inRecordType
, aRecordId
, inoutUniqueRecord
);
2083 mDbModifier
.rollback();
2087 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2088 mDbModifier
.commit();
2092 AppleDatabase::dataGetFirst(DbContext
&inDbContext
,
2093 const DLQuery
*inQuery
,
2094 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
2095 CssmData
*inoutData
,
2096 CSSM_DB_UNIQUE_RECORD_PTR
&outUniqueRecord
)
2098 // XXX: register Cursor with DbContext and have DbContext call
2099 // dataAbortQuery for all outstanding Query objects on close.
2100 auto_ptr
<Cursor
> aCursor(mDbModifier
.createCursor(inQuery
));
2104 if (!aCursor
->next(aTableId
, inoutAttributes
, inoutData
,
2105 inDbContext
.mDatabaseSession
, aRecordId
))
2106 // return a NULL handle, and implicitly delete the cursor
2107 return CSSM_INVALID_HANDLE
;
2109 outUniqueRecord
= createUniqueRecord(inDbContext
, aTableId
, aRecordId
);
2110 return aCursor
.release()->handle(); // We didn't throw so keep the Cursor around.
2114 AppleDatabase::dataGetNext(DbContext
&inDbContext
,
2115 CSSM_HANDLE inResultsHandle
,
2116 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
2117 CssmData
*inoutData
,
2118 CSSM_DB_UNIQUE_RECORD_PTR
&outUniqueRecord
)
2120 auto_ptr
<Cursor
> aCursor(&findHandle
<Cursor
>(inResultsHandle
, CSSMERR_DL_INVALID_RESULTS_HANDLE
));
2124 if (!aCursor
->next(aTableId
, inoutAttributes
, inoutData
, inDbContext
.mDatabaseSession
, aRecordId
))
2127 outUniqueRecord
= createUniqueRecord(inDbContext
, aTableId
, aRecordId
);
2134 AppleDatabase::dataAbortQuery(DbContext
&inDbContext
,
2135 CSSM_HANDLE inResultsHandle
)
2137 delete &findHandle
<Cursor
>(inResultsHandle
, CSSMERR_DL_INVALID_RESULTS_HANDLE
);
2141 AppleDatabase::dataGetFromUniqueRecordId(DbContext
&inDbContext
,
2142 const CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
,
2143 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
2144 CssmData
*inoutData
)
2147 const RecordId
aRecordId(parseUniqueRecord(inUniqueRecord
, aTableId
));
2148 // XXX Change CDSA spec to use new RecordId returned by this function
2149 mDbModifier
.getRecord(aTableId
, aRecordId
, inoutAttributes
, inoutData
,
2150 inDbContext
.mDatabaseSession
);
2154 AppleDatabase::freeUniqueRecord(DbContext
&inDbContext
,
2155 CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
)
2157 if (inUniqueRecord
.RecordIdentifier
.Length
!= 0
2158 && inUniqueRecord
.RecordIdentifier
.Data
!= NULL
)
2160 inUniqueRecord
.RecordIdentifier
.Length
= 0;
2161 inDbContext
.mDatabaseSession
.free(inUniqueRecord
.RecordIdentifier
.Data
);
2163 inDbContext
.mDatabaseSession
.free(&inUniqueRecord
);
2167 AppleDatabase::updateUniqueRecord(DbContext
&inDbContext
,
2168 CSSM_DB_RECORDTYPE inTableId
,
2169 const RecordId
&inRecordId
,
2170 CSSM_DB_UNIQUE_RECORD
&inoutUniqueRecord
)
2172 uint32
*aBuffer
= reinterpret_cast<uint32
*>(inoutUniqueRecord
.RecordIdentifier
.Data
);
2173 aBuffer
[0] = inTableId
;
2174 aBuffer
[1] = inRecordId
.mRecordNumber
;
2175 aBuffer
[2] = inRecordId
.mCreateVersion
;
2176 aBuffer
[3] = inRecordId
.mRecordVersion
;
2179 CSSM_DB_UNIQUE_RECORD_PTR
2180 AppleDatabase::createUniqueRecord(DbContext
&inDbContext
,
2181 CSSM_DB_RECORDTYPE inTableId
,
2182 const RecordId
&inRecordId
)
2184 CSSM_DB_UNIQUE_RECORD_PTR aUniqueRecord
=
2185 inDbContext
.mDatabaseSession
.alloc
<CSSM_DB_UNIQUE_RECORD
>();
2186 memset(aUniqueRecord
, 0, sizeof(*aUniqueRecord
));
2187 aUniqueRecord
->RecordIdentifier
.Length
= sizeof(uint32
) * 4;
2190 aUniqueRecord
->RecordIdentifier
.Data
=
2191 inDbContext
.mDatabaseSession
.alloc
<uint8
>(sizeof(uint32
) * 4);
2192 updateUniqueRecord(inDbContext
, inTableId
, inRecordId
, *aUniqueRecord
);
2196 inDbContext
.mDatabaseSession
.free(aUniqueRecord
);
2200 return aUniqueRecord
;
2204 AppleDatabase::parseUniqueRecord(const CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
,
2205 CSSM_DB_RECORDTYPE
&outTableId
)
2207 if (inUniqueRecord
.RecordIdentifier
.Length
!= sizeof(uint32
) * 4)
2208 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID
);
2210 uint32
*aBuffer
= reinterpret_cast<uint32
*>(inUniqueRecord
.RecordIdentifier
.Data
);
2211 outTableId
= aBuffer
[0];
2212 return RecordId(aBuffer
[1], aBuffer
[2], aBuffer
[3]);
2216 AppleDatabase::passThrough(DbContext
&dbContext
,
2217 uint32 passThroughId
,
2218 const void *inputParams
,
2219 void **outputParams
)
2221 switch (passThroughId
)
2223 case CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
:
2225 CSSM_BOOL on
= reinterpret_cast<CSSM_BOOL
>(inputParams
);
2226 safer_cast
<AppleDbContext
&>(dbContext
).autoCommit(on
);
2230 case CSSM_APPLEFILEDL_COMMIT
:
2231 mDbModifier
.commit();
2234 case CSSM_APPLEFILEDL_ROLLBACK
:
2235 mDbModifier
.rollback();
2239 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);