2 * Copyright (c) 2000-2001, 2003 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(uint32 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(AtomicTempFile
&inAtomicTempFile
, 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 inAtomicTempFile
.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 inAtomicTempFile
.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 inAtomicTempFile
.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 inAtomicTempFile
.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 inAtomicTempFile
.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 inAtomicTempFile
.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(const AppleDatabase
&db
, const RefPointer
<AtomicBufferedFile
> &inAtomicBufferedFile
) :
743 mDatabase(reinterpret_cast<const uint8
*>(NULL
), 0),
745 mBufferedFile(inAtomicBufferedFile
)
747 off_t aLength
= mBufferedFile
->length();
749 const uint8
*ptr
= mBufferedFile
->read(0, aLength
, bytesRead
);
750 mBufferedFile
->close();
751 mDatabase
= ReadSection(ptr
, bytesRead
);
755 DbVersion::~DbVersion()
759 for_each_map_delete(mTableMap
.begin(), mTableMap
.end());
769 // This is the oposite of DbModifier::commit()
770 mVersionId
= mDatabase
[mDatabase
.size() - AtomSize
];
772 const ReadSection aHeaderSection
= mDatabase
.subsection(HeaderOffset
,
774 if (aHeaderSection
.at(OffsetMagic
) != HeaderMagic
)
775 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
777 // We currently only support one version. If we support additional
778 // file format versions in the future fix this.
779 uint32 aVersion
= aHeaderSection
.at(OffsetVersion
);
780 if (aVersion
!= HeaderVersion
)
781 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
783 //const ReadSection anAuthSection =
784 // mDatabase.subsection(HeaderOffset + aHeaderSection.at(OffsetAuthOffset));
785 // XXX Do something with anAuthSection.
787 uint32 aSchemaOffset
= aHeaderSection
.at(OffsetSchemaOffset
);
788 const ReadSection aSchemaSection
=
789 mDatabase
.subsection(HeaderOffset
+ aSchemaOffset
);
791 uint32 aSchemaSize
= aSchemaSection
[OffsetSchemaSize
];
792 // Make sure that the given range exists.
793 aSchemaSection
.subsection(0, aSchemaSize
);
794 uint32 aTableCount
= aSchemaSection
[OffsetTablesCount
];
796 // Assert that the size of this section is big enough.
797 if (aSchemaSize
< OffsetTables
+ AtomSize
* aTableCount
)
798 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
800 for (uint32 aTableNumber
= 0; aTableNumber
< aTableCount
;
803 uint32 aTableOffset
= aSchemaSection
.at(OffsetTables
+ AtomSize
805 // XXX Set the size boundary on aTableSection.
806 const ReadSection aTableSection
=
807 aSchemaSection
.subsection(aTableOffset
);
808 auto_ptr
<Table
> aTable(new Table(aTableSection
));
809 Table::Id aTableId
= aTable
->getMetaRecord().dataRecordType();
810 mTableMap
.insert(TableMap::value_type(aTableId
, aTable
.get()));
814 // Fill in the schema for the meta tables.
816 findTable(mDb
.schemaRelations
.DataRecordType
).getMetaRecord().
817 setRecordAttributeInfo(mDb
.schemaRelations
);
818 findTable(mDb
.schemaIndexes
.DataRecordType
).getMetaRecord().
819 setRecordAttributeInfo(mDb
.schemaIndexes
);
820 findTable(mDb
.schemaParsingModule
.DataRecordType
).getMetaRecord().
821 setRecordAttributeInfo(mDb
.schemaParsingModule
);
823 // OK, we have created all the tables in the tableMap. Now
824 // lets read the schema and proccess it accordingly.
825 // Iterate over all schema records.
826 Table
&aTable
= findTable(mDb
.schemaAttributes
.DataRecordType
);
827 aTable
.getMetaRecord().setRecordAttributeInfo(mDb
.schemaAttributes
);
828 uint32 aRecordsCount
= aTable
.getRecordsCount();
829 ReadSection aRecordsSection
= aTable
.getRecordsSection();
830 uint32 aReadOffset
= 0;
831 const MetaRecord
&aMetaRecord
= aTable
.getMetaRecord();
833 CSSM_DB_ATTRIBUTE_DATA aRelationIDData
=
839 CSSM_DB_ATTRIBUTE_DATA aAttributeIDData
=
845 CSSM_DB_ATTRIBUTE_DATA aAttributeNameFormatData
=
851 CSSM_DB_ATTRIBUTE_DATA aAttributeNameData
=
857 CSSM_DB_ATTRIBUTE_DATA aAttributeNameIDData
=
863 CSSM_DB_ATTRIBUTE_DATA aAttributeFormatData
=
869 CSSM_DB_ATTRIBUTE_DATA aRecordAttributes
[] =
873 aAttributeNameFormatData
,
875 aAttributeNameIDData
,
878 CSSM_DB_RECORD_ATTRIBUTE_DATA aRecordAttributeData
=
880 aMetaRecord
.dataRecordType(),
882 sizeof(aRecordAttributes
) / sizeof(CSSM_DB_ATTRIBUTE_DATA
),
885 CssmDbRecordAttributeData
&aRecordData
= CssmDbRecordAttributeData::overlay(aRecordAttributeData
);
887 TrackingAllocator
recordAllocator(CssmAllocator::standard());
888 for (uint32 aRecord
= 0; aRecord
!= aRecordsCount
; aRecord
++)
890 ReadSection aRecordSection
= MetaRecord::readSection(aRecordsSection
, aReadOffset
);
891 uint32 aRecordSize
= aRecordSection
.size();
892 aReadOffset
+= aRecordSize
;
893 aMetaRecord
.unpackRecord(aRecordSection
, recordAllocator
,
894 &aRecordAttributeData
, NULL
, 0);
895 // Create the attribute coresponding to this entry
896 if (aRecordData
[0].size() != 1 || aRecordData
[0].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
)
897 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
898 uint32 aRelationId
= aRecordData
[0];
900 // Skip the schema relations for the meta tables themselves.
901 // FIXME: this hard-wires the meta-table relation IDs to be
902 // within {CSSM_DB_RECORDTYPE_SCHEMA_START...
903 // CSSM_DB_RECORDTYPE_SCHEMA_END} (which is {0..4}).
904 // Bogus - the MDS schema relation IDs start at
905 // CSSM_DB_RELATIONID_MDS_START which is 0x40000000.
906 // Ref. Radar 2817921.
907 if (CSSM_DB_RECORDTYPE_SCHEMA_START
<= aRelationId
&& aRelationId
< CSSM_DB_RECORDTYPE_SCHEMA_END
)
910 // Get the MetaRecord corresponding to the specified RelationId
911 MetaRecord
&aMetaRecord
= findTable(aRelationId
).getMetaRecord();
913 if (aRecordData
[1].size() != 1
914 || aRecordData
[1].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
915 || aRecordData
[2].size() != 1
916 || aRecordData
[2].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
917 || aRecordData
[5].size() != 1
918 || aRecordData
[5].format() != CSSM_DB_ATTRIBUTE_FORMAT_UINT32
)
919 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
921 uint32 anAttributeId
= aRecordData
[1];
922 uint32 anAttributeNameFormat
= aRecordData
[2];
923 uint32 anAttributeFormat
= aRecordData
[5];
924 auto_ptr
<string
> aName
;
925 const CssmData
*aNameID
= NULL
;
927 if (aRecordData
[3].size() == 1)
929 if (aRecordData
[3].format() != CSSM_DB_ATTRIBUTE_FORMAT_STRING
)
930 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
932 auto_ptr
<string
> aName2(new string(static_cast<string
>(aRecordData
[3])));
936 if (aRecordData
[4].size() == 1)
938 if (aRecordData
[4].format() != CSSM_DB_ATTRIBUTE_FORMAT_BLOB
)
939 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
941 // @@@ Invoking conversion operator to CssmData & on aRecordData[4]
942 // And taking address of result.
943 aNameID
= &static_cast<CssmData
&>(aRecordData
[4]);
946 // Make sure that the attribute specified by anAttributeNameFormat is present.
947 switch (anAttributeNameFormat
)
949 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING
:
950 if (aRecordData
[3].size() != 1)
951 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
953 case CSSM_DB_ATTRIBUTE_NAME_AS_OID
:
954 if (aRecordData
[4].size() != 1)
955 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
957 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
:
960 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
963 // Create the attribute
964 aMetaRecord
.createAttribute(aName
.get(), aNameID
, anAttributeId
, anAttributeFormat
);
967 // initialize the indexes associated with each table
969 TableMap::iterator it
;
970 for (it
= mTableMap
.begin(); it
!= mTableMap
.end(); it
++)
971 it
->second
->readIndexSection();
976 for_each_map_delete(mTableMap
.begin(), mTableMap
.end());
983 DbVersion::getRecord(Table::Id inTableId
, const RecordId
&inRecordId
,
984 CSSM_DB_RECORD_ATTRIBUTE_DATA
*inoutAttributes
,
986 CssmAllocator
&inAllocator
) const
988 return findTable(inTableId
).getRecord(inRecordId
, inoutAttributes
,
989 inoutData
, inAllocator
);
993 DbVersion::createCursor(const CSSM_QUERY
*inQuery
) const
995 // XXX We should add support for these special query types
996 // By Creating a Cursor that iterates over multiple tables
997 if (!inQuery
|| inQuery
->RecordType
== CSSM_DL_DB_RECORD_ANY
998 || inQuery
->RecordType
== CSSM_DL_DB_RECORD_ALL_KEYS
)
1000 return new MultiCursor(inQuery
, *this);
1003 return findTable(inQuery
->RecordType
).createCursor(inQuery
, *this);
1007 DbVersion::findTable(Table::Id inTableId
) const
1009 TableMap::const_iterator it
= mTableMap
.find(inTableId
);
1010 if (it
== mTableMap
.end())
1011 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1016 DbVersion::findTable(Table::Id inTableId
)
1018 TableMap::iterator it
= mTableMap
.find(inTableId
);
1019 if (it
== mTableMap
.end())
1020 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1025 // Cursor implemetation
1033 // LinearCursor implemetation
1035 LinearCursor::LinearCursor(const CSSM_QUERY
*inQuery
, const DbVersion
&inDbVersion
,
1036 const Table
&inTable
) :
1037 mDbVersion(&inDbVersion
),
1038 mRecordsCount(inTable
.getRecordsCount()),
1040 mRecordsSection(inTable
.getRecordsSection()),
1042 mMetaRecord(inTable
.getMetaRecord())
1046 mConjunctive
= inQuery
->Conjunctive
;
1047 mQueryFlags
= inQuery
->QueryFlags
;
1048 // XXX Do something with inQuery->QueryLimits?
1049 uint32 aPredicatesCount
= inQuery
->NumSelectionPredicates
;
1050 mPredicates
.resize(aPredicatesCount
);
1053 for (uint32 anIndex
= 0; anIndex
< aPredicatesCount
; anIndex
++)
1055 CSSM_SELECTION_PREDICATE
&aPredicate
= inQuery
->SelectionPredicate
[anIndex
];
1056 mPredicates
[anIndex
] = new SelectionPredicate(mMetaRecord
, aPredicate
);
1061 for_each_delete(mPredicates
.begin(), mPredicates
.end());
1067 LinearCursor::~LinearCursor()
1069 for_each_delete(mPredicates
.begin(), mPredicates
.end());
1073 LinearCursor::next(Table::Id
&outTableId
,
1074 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
1075 CssmData
*inoutData
, CssmAllocator
&inAllocator
, RecordId
&recordId
)
1077 while (mRecord
++ < mRecordsCount
)
1079 ReadSection aRecordSection
= MetaRecord::readSection(mRecordsSection
, mReadOffset
);
1080 uint32 aRecordSize
= aRecordSection
.size();
1081 mReadOffset
+= aRecordSize
;
1083 PredicateVector::const_iterator anIt
= mPredicates
.begin();
1084 PredicateVector::const_iterator anEnd
= mPredicates
.end();
1088 // If there are no predicates we have a match.
1091 else if (mConjunctive
== CSSM_DB_OR
)
1093 // If mConjunctive is OR, the first predicate that returns
1094 // true indicates a match. Dropthough means no match
1096 for (; anIt
!= anEnd
; anIt
++)
1098 if ((*anIt
)->evaluate(aRecordSection
))
1105 else if (mConjunctive
== CSSM_DB_AND
|| mConjunctive
== CSSM_DB_NONE
)
1107 // If mConjunctive is AND (or NONE), the first predicate that returns
1108 // false indicates a mismatch. Dropthough means a match
1110 for (; anIt
!= anEnd
; anIt
++)
1112 if (!(*anIt
)->evaluate(aRecordSection
))
1121 // XXX Should be CSSMERR_DL_INVALID_QUERY (or CSSMERR_DL_INVALID_CONJUNTIVE).
1122 CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_QUERY
);
1127 // Get the actual record.
1128 mMetaRecord
.unpackRecord(aRecordSection
, inAllocator
,
1129 inoutAttributes
, inoutData
,
1131 outTableId
= mMetaRecord
.dataRecordType();
1132 recordId
= MetaRecord::unpackRecordId(aRecordSection
);
1144 IndexCursor::IndexCursor(DbQueryKey
*queryKey
, const DbVersion
&inDbVersion
,
1145 const Table
&table
, const DbConstIndex
*index
)
1146 : mQueryKey(queryKey
), mDbVersion(inDbVersion
), mTable(table
), mIndex(index
)
1148 index
->performQuery(*queryKey
, mBegin
, mEnd
);
1151 IndexCursor::~IndexCursor()
1153 // the query key will be deleted automatically, since it's an auto_ptr
1157 IndexCursor::next(Table::Id
&outTableId
,
1158 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR outAttributes
,
1160 CssmAllocator
&inAllocator
, RecordId
&recordId
)
1165 ReadSection rs
= mIndex
->getRecordSection(mBegin
++);
1166 const MetaRecord
&metaRecord
= mTable
.getMetaRecord();
1168 outTableId
= metaRecord
.dataRecordType();
1169 metaRecord
.unpackRecord(rs
, inAllocator
, outAttributes
, outData
, 0);
1171 recordId
= MetaRecord::unpackRecordId(rs
);
1178 MultiCursor::MultiCursor(const CSSM_QUERY
*inQuery
, const DbVersion
&inDbVersion
) :
1179 mDbVersion(&inDbVersion
), mTableIterator(inDbVersion
.begin())
1182 mQuery
.reset(new CssmAutoQuery(*inQuery
));
1185 mQuery
.reset(new CssmAutoQuery());
1186 mQuery
->recordType(CSSM_DL_DB_RECORD_ANY
);
1190 MultiCursor::~MultiCursor()
1195 MultiCursor::next(Table::Id
&outTableId
,
1196 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
1197 CssmData
*inoutData
, CssmAllocator
&inAllocator
, RecordId
&recordId
)
1203 if (mTableIterator
== mDbVersion
->end())
1206 const Table
&aTable
= *mTableIterator
++;
1207 if (!aTable
.matchesTableId(mQuery
->recordType()))
1210 mCursor
.reset(aTable
.createCursor(mQuery
.get(), *mDbVersion
));
1213 if (mCursor
->next(outTableId
, inoutAttributes
, inoutData
, inAllocator
, recordId
))
1216 mCursor
.reset(NULL
);
1224 DbModifier::DbModifier(AtomicFile
&inAtomicFile
, const AppleDatabase
&db
) :
1227 mAtomicFile(inAtomicFile
),
1232 DbModifier::~DbModifier()
1236 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1237 // mAtomicTempFile will do automatic rollback on destruction.
1242 const RefPointer
<const DbVersion
>
1243 DbModifier::getDbVersion()
1245 StLock
<Mutex
> _(mDbVersionLock
);
1246 RefPointer
<AtomicBufferedFile
> atomicBufferedFile(mAtomicFile
.read());
1247 off_t length
= atomicBufferedFile
->open();
1250 if (length
< AtomSize
)
1251 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
1253 off_t bytesRead
= 0;
1254 const uint8
*ptr
= atomicBufferedFile
->read(length
- AtomSize
, AtomSize
, bytesRead
);
1255 ReadSection
aVersionSection(ptr
, bytesRead
);
1256 uint32 aVersionId
= aVersionSection
[0];
1258 if (aVersionId
== mDbVersion
->getVersionId())
1262 mDbVersion
= new DbVersion(mDb
, atomicBufferedFile
);
1268 DbModifier::createDatabase(const CSSM_DBINFO
&inDbInfo
,
1269 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 (mAtomicTempFile
|| !mModifiedTableMap
.empty())
1275 CssmError::throwMe(CSSMERR_DL_DATASTORE_ALREADY_EXISTS
);
1277 mAtomicTempFile
= mAtomicFile
.create(mode
);
1278 // Set mVersionId to one since this is the first version of the database.
1281 // we need to create the meta tables first, because inserting tables
1282 // (including the meta tables themselves) relies on them being there
1283 createTable(new MetaRecord(mDb
.schemaRelations
));
1284 createTable(new MetaRecord(mDb
.schemaAttributes
));
1285 createTable(new MetaRecord(mDb
.schemaIndexes
));
1286 createTable(new MetaRecord(mDb
.schemaParsingModule
));
1288 // now add the meta-tables' schema to the meta tables themselves
1289 insertTableSchema(mDb
.schemaRelations
);
1290 insertTableSchema(mDb
.schemaAttributes
);
1291 insertTableSchema(mDb
.schemaIndexes
);
1292 insertTableSchema(mDb
.schemaParsingModule
);
1294 if (inInitialAclEntry
!= NULL
)
1296 //createACL(*inInitialAclEntry);
1299 if (inDbInfo
.NumberOfRecordTypes
== 0)
1301 if (inDbInfo
.RecordAttributeNames
== NULL
)
1302 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1303 if (inDbInfo
.RecordIndexes
== NULL
)
1304 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_INDEX
);
1305 if (inDbInfo
.DefaultParsingModules
== NULL
)
1306 CssmError::throwMe(CSSMERR_DL_INVALID_PARSING_MODULE
);
1308 for (uint32 anIndex
= 0; anIndex
< inDbInfo
.NumberOfRecordTypes
; anIndex
++)
1310 insertTable(CssmDbRecordAttributeInfo::overlay(inDbInfo
.RecordAttributeNames
[anIndex
]),
1311 &inDbInfo
.RecordIndexes
[anIndex
],
1312 &inDbInfo
.DefaultParsingModules
[anIndex
]);
1316 void DbModifier::openDatabase()
1318 commit(); // XXX Requires write lock.
1322 void DbModifier::closeDatabase()
1324 commit(); // XXX Requires write lock.
1325 StLock
<Mutex
> _(mDbVersionLock
);
1329 void DbModifier::deleteDatabase()
1331 rollback(); // XXX Requires write lock. Also if autoCommit was disabled
1332 // this will incorrectly cause the performDelete to throw CSSMERR_DB_DOES_NOT_EXIST.
1333 StLock
<Mutex
> _(mDbVersionLock
);
1335 // Clean up mModifiedTableMap in case this object gets reused again for
1337 for_each_map_delete(mModifiedTableMap
.begin(), mModifiedTableMap
.end());
1338 mModifiedTableMap
.clear();
1341 mAtomicFile
.performDelete();
1345 DbModifier::modifyDatabase()
1347 if (mAtomicTempFile
)
1352 mAtomicTempFile
= mAtomicFile
.write();
1353 // Now we are holding the write lock make sure we get the latest greatest version of the db.
1354 // Also set mVersionId to one more that that of the old database.
1355 mVersionId
= getDbVersion()->getVersionId() + 1;
1357 // Never make a database with mVersionId 0 since it makes bad things happen to Jaguar and older systems
1358 if (mVersionId
== 0)
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 // @@@ Investigate why update is forcing a commit unlike delete and insert?
1410 commit(); // XXX this is not thread safe, but what is?
1412 return findTable(inTableId
).updateRecord(inRecordId
, inAttributes
, inData
, inModifyMode
);
1415 // Create a table associated with a given metarecord, and add the table
1419 DbModifier::createTable(MetaRecord
*inMetaRecord
)
1421 auto_ptr
<MetaRecord
> aMetaRecord(inMetaRecord
);
1422 auto_ptr
<ModifiedTable
> aModifiedTable(new ModifiedTable(inMetaRecord
));
1423 // Now that aModifiedTable is fully constructed it owns inMetaRecord
1424 aMetaRecord
.release();
1426 if (!mModifiedTableMap
.insert
1427 (ModifiedTableMap::value_type(inMetaRecord
->dataRecordType(),
1428 aModifiedTable
.get())).second
)
1430 // XXX Should be CSSMERR_DL_DUPLICATE_RECORDTYPE. Since that
1431 // doesn't exist we report that the metatable's unique index would
1432 // no longer be valid
1433 CssmError::throwMe(CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
);
1436 return aModifiedTable
.release();
1440 DbModifier::deleteTable(Table::Id inTableId
)
1443 // Can't delete schema tables.
1444 if (CSSM_DB_RECORDTYPE_SCHEMA_START
<= inTableId
1445 && inTableId
< CSSM_DB_RECORDTYPE_SCHEMA_END
)
1446 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1448 // Find the ModifiedTable and delete it
1449 ModifiedTableMap::iterator it
= mModifiedTableMap
.find(inTableId
);
1450 if (it
== mModifiedTableMap
.end())
1451 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1454 mModifiedTableMap
.erase(it
);
1458 DbModifier::writeAuthSection(uint32 inSectionOffset
)
1460 WriteSection anAuthSection
;
1462 // XXX Put real data into the authsection.
1463 uint32 anOffset
= anAuthSection
.put(0, 0);
1464 anAuthSection
.size(anOffset
);
1466 mAtomicTempFile
->write(AtomicFile::FromStart
, inSectionOffset
,
1467 anAuthSection
.address(), anAuthSection
.size());
1468 return inSectionOffset
+ anOffset
;
1472 DbModifier::writeSchemaSection(uint32 inSectionOffset
)
1474 uint32 aTableCount
= mModifiedTableMap
.size();
1475 WriteSection
aTableSection(CssmAllocator::standard(),
1476 OffsetTables
+ AtomSize
* aTableCount
);
1477 // Set aTableSection to the correct size.
1478 aTableSection
.size(OffsetTables
+ AtomSize
* aTableCount
);
1479 aTableSection
.put(OffsetTablesCount
, aTableCount
);
1481 uint32 anOffset
= inSectionOffset
+ OffsetTables
+ AtomSize
* aTableCount
;
1482 ModifiedTableMap::const_iterator anIt
= mModifiedTableMap
.begin();
1483 ModifiedTableMap::const_iterator anEnd
= mModifiedTableMap
.end();
1484 for (uint32 aTableNumber
= 0; anIt
!= anEnd
; anIt
++, aTableNumber
++)
1486 // Put the offset to the current table relative to the start of
1487 // this section into the tables array
1488 aTableSection
.put(OffsetTables
+ AtomSize
* aTableNumber
,
1489 anOffset
- inSectionOffset
);
1490 anOffset
= anIt
->second
->writeTable(*mAtomicTempFile
, anOffset
);
1493 aTableSection
.put(OffsetSchemaSize
, anOffset
- inSectionOffset
);
1494 mAtomicTempFile
->write(AtomicFile::FromStart
, inSectionOffset
,
1495 aTableSection
.address(), aTableSection
.size());
1501 DbModifier::commit()
1503 if (!mAtomicTempFile
)
1507 WriteSection
aHeaderSection(CssmAllocator::standard(), size_t(HeaderSize
));
1508 // Set aHeaderSection to the correct size.
1509 aHeaderSection
.size(HeaderSize
);
1511 // Start writing sections after the header
1512 uint32 anOffset
= HeaderOffset
+ HeaderSize
;
1514 // Write auth section
1515 aHeaderSection
.put(OffsetAuthOffset
, anOffset
);
1516 anOffset
= writeAuthSection(anOffset
);
1517 // Write schema section
1518 aHeaderSection
.put(OffsetSchemaOffset
, anOffset
);
1519 anOffset
= writeSchemaSection(anOffset
);
1521 // Write out the file header.
1522 aHeaderSection
.put(OffsetMagic
, HeaderMagic
);
1523 aHeaderSection
.put(OffsetVersion
, HeaderVersion
);
1524 mAtomicTempFile
->write(AtomicFile::FromStart
, HeaderOffset
,
1525 aHeaderSection
.address(), aHeaderSection
.size());
1527 // Write out the versionId.
1528 WriteSection
aVersionSection(CssmAllocator::standard(), size_t(AtomSize
));
1529 anOffset
= aVersionSection
.put(0, mVersionId
);
1530 aVersionSection
.size(anOffset
);
1532 mAtomicTempFile
->write(AtomicFile::FromEnd
, 0,
1533 aVersionSection
.address(), aVersionSection
.size());
1535 mAtomicTempFile
->commit();
1536 mAtomicTempFile
= NULL
;
1546 DbModifier::rollback() throw()
1548 // This will destroy the AtomicTempFile if we have one causing it to rollback.
1549 mAtomicTempFile
= NULL
;
1553 DbModifier::getRecord(Table::Id inTableId
, const RecordId
&inRecordId
,
1554 CSSM_DB_RECORD_ATTRIBUTE_DATA
*inoutAttributes
,
1555 CssmData
*inoutData
, CssmAllocator
&inAllocator
)
1557 // XXX never call commit(), rather search our own record tables.
1558 commit(); // XXX Requires write lock.
1559 return getDbVersion()->getRecord(inTableId
, inRecordId
,
1560 inoutAttributes
, inoutData
, inAllocator
);
1564 DbModifier::createCursor(const CSSM_QUERY
*inQuery
)
1566 // XXX Be smarter as to when we must call commit (i.e. don't
1567 // force commit if the table being queried has not been modified).
1568 commit(); // XXX Requires write lock.
1569 return getDbVersion()->createCursor(inQuery
);
1572 // Insert schema records for a new table into the metatables of the database. This gets
1573 // called while a database is being created.
1576 DbModifier::insertTableSchema(const CssmDbRecordAttributeInfo
&inInfo
,
1577 const CSSM_DB_RECORD_INDEX_INFO
*inIndexInfo
/* = NULL */)
1579 ModifiedTable
&aTable
= findTable(inInfo
.DataRecordType
);
1580 const MetaRecord
&aMetaRecord
= aTable
.getMetaRecord();
1582 CssmAutoDbRecordAttributeData
aRecordBuilder(5); // Set capacity to 5 so we don't need to grow
1584 // Create the entry for the SchemaRelations table.
1585 aRecordBuilder
.add(RelationID
, inInfo
.recordType());
1586 aRecordBuilder
.add(RelationName
, mDb
.recordName(inInfo
.recordType()));
1588 // Insert the record into the SchemaRelations ModifiedTable
1589 findTable(mDb
.schemaRelations
.DataRecordType
).insertRecord(mVersionId
,
1590 &aRecordBuilder
, NULL
);
1592 ModifiedTable
&anAttributeTable
= findTable(mDb
.schemaAttributes
.DataRecordType
);
1593 for (uint32 anIndex
= 0; anIndex
< inInfo
.size(); anIndex
++)
1595 // Create an entry for the SchemaAttributes table.
1596 aRecordBuilder
.clear();
1597 aRecordBuilder
.add(RelationID
, inInfo
.recordType());
1598 aRecordBuilder
.add(AttributeNameFormat
, inInfo
.at(anIndex
).nameFormat());
1600 uint32 attributeId
= aMetaRecord
.metaAttribute(inInfo
.at(anIndex
)).attributeId();
1602 switch (inInfo
.at(anIndex
).nameFormat())
1604 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING
:
1605 aRecordBuilder
.add(AttributeName
, inInfo
.at(anIndex
).Label
.AttributeName
);
1607 case CSSM_DB_ATTRIBUTE_NAME_AS_OID
:
1608 aRecordBuilder
.add(AttributeNameID
, inInfo
.at(anIndex
).Label
.AttributeOID
);
1610 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
:
1613 CssmError::throwMe(CSSMERR_DL_INVALID_FIELD_NAME
);
1616 aRecordBuilder
.add(AttributeID
, attributeId
);
1617 aRecordBuilder
.add(AttributeFormat
, inInfo
.at(anIndex
).format());
1619 // Insert the record into the SchemaAttributes ModifiedTable
1620 anAttributeTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1623 if (inIndexInfo
!= NULL
) {
1625 if (inIndexInfo
->DataRecordType
!= inInfo
.DataRecordType
&&
1626 inIndexInfo
->NumberOfIndexes
> 0)
1627 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1629 ModifiedTable
&indexMetaTable
= findTable(mDb
.schemaIndexes
.DataRecordType
);
1630 uint32 aNumberOfIndexes
= inIndexInfo
->NumberOfIndexes
;
1632 for (uint32 anIndex
= 0; anIndex
< aNumberOfIndexes
; anIndex
++)
1634 const CssmDbIndexInfo
&thisIndex
= CssmDbIndexInfo::overlay(inIndexInfo
->IndexInfo
[anIndex
]);
1636 // make sure the index is supported
1637 if (thisIndex
.dataLocation() != CSSM_DB_INDEX_ON_ATTRIBUTE
)
1638 CssmError::throwMe(CSSMERR_DL_INVALID_INDEX_INFO
);
1640 // assign an index ID: the unique index is ID 0, all others are ID > 0
1642 if (thisIndex
.IndexType
== CSSM_DB_INDEX_UNIQUE
)
1645 indexId
= anIndex
+ 1;
1647 // figure out the attribute ID
1648 uint32 attributeId
=
1649 aMetaRecord
.metaAttribute(thisIndex
.Info
).attributeId();
1651 // Create an entry for the SchemaIndexes table.
1652 aRecordBuilder
.clear();
1653 aRecordBuilder
.add(RelationID
, inInfo
.DataRecordType
);
1654 aRecordBuilder
.add(IndexID
, indexId
);
1655 aRecordBuilder
.add(AttributeID
, attributeId
);
1656 aRecordBuilder
.add(IndexType
, thisIndex
.IndexType
);
1657 aRecordBuilder
.add(IndexedDataLocation
, thisIndex
.IndexedDataLocation
);
1659 // Insert the record into the SchemaIndexes ModifiedTable
1660 indexMetaTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1662 // update the table's index objects
1663 DbMutableIndex
&index
= aTable
.findIndex(indexId
, aMetaRecord
, indexId
== 0);
1664 index
.appendAttribute(attributeId
);
1669 // Insert a new table. The attribute info is required; the index and parsing module
1670 // descriptions are optional. This version gets called during the creation of a
1674 DbModifier::insertTable(const CssmDbRecordAttributeInfo
&inInfo
,
1675 const CSSM_DB_RECORD_INDEX_INFO
*inIndexInfo
/* = NULL */,
1676 const CSSM_DB_PARSING_MODULE_INFO
*inParsingModule
/* = NULL */)
1679 createTable(new MetaRecord(inInfo
));
1680 insertTableSchema(inInfo
, inIndexInfo
);
1683 // Insert a new table. This is the version that gets called when a table is added
1684 // after a database has been created.
1687 DbModifier::insertTable(Table::Id inTableId
, const string
&inTableName
,
1688 uint32 inNumberOfAttributes
,
1689 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
*inAttributeInfo
,
1690 uint32 inNumberOfIndexes
,
1691 const CSSM_DB_SCHEMA_INDEX_INFO
*inIndexInfo
)
1694 ModifiedTable
*aTable
= createTable(new MetaRecord(inTableId
, inNumberOfAttributes
, inAttributeInfo
));
1696 CssmAutoDbRecordAttributeData
aRecordBuilder(6); // Set capacity to 6 so we don't need to grow
1698 // Create the entry for the SchemaRelations table.
1699 aRecordBuilder
.add(RelationID
, inTableId
);
1700 aRecordBuilder
.add(RelationName
, inTableName
);
1702 // Insert the record into the SchemaRelations ModifiedTable
1703 findTable(mDb
.schemaRelations
.DataRecordType
).insertRecord(mVersionId
,
1704 &aRecordBuilder
, NULL
);
1706 ModifiedTable
&anAttributeTable
= findTable(mDb
.schemaAttributes
.DataRecordType
);
1707 for (uint32 anIndex
= 0; anIndex
< inNumberOfAttributes
; anIndex
++)
1709 // Create an entry for the SchemaAttributes table.
1710 aRecordBuilder
.clear();
1711 aRecordBuilder
.add(RelationID
, inTableId
);
1712 // XXX What should this be? We set it to CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER for now
1713 // since the AttributeID is always valid.
1714 aRecordBuilder
.add(AttributeNameFormat
, uint32(CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
));
1715 aRecordBuilder
.add(AttributeID
, inAttributeInfo
[anIndex
].AttributeId
);
1716 if (inAttributeInfo
[anIndex
].AttributeName
)
1717 aRecordBuilder
.add(AttributeName
, inAttributeInfo
[anIndex
].AttributeName
);
1718 if (inAttributeInfo
[anIndex
].AttributeNameID
.Length
> 0)
1719 aRecordBuilder
.add(AttributeNameID
, inAttributeInfo
[anIndex
].AttributeNameID
);
1720 aRecordBuilder
.add(AttributeFormat
, inAttributeInfo
[anIndex
].DataType
);
1722 // Insert the record into the SchemaAttributes ModifiedTable
1723 anAttributeTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1726 ModifiedTable
&anIndexTable
= findTable(mDb
.schemaIndexes
.DataRecordType
);
1727 for (uint32 anIndex
= 0; anIndex
< inNumberOfIndexes
; anIndex
++)
1729 // Create an entry for the SchemaIndexes table.
1730 aRecordBuilder
.clear();
1731 aRecordBuilder
.add(RelationID
, inTableId
);
1732 aRecordBuilder
.add(IndexID
, inIndexInfo
[anIndex
].IndexId
);
1733 aRecordBuilder
.add(AttributeID
, inIndexInfo
[anIndex
].AttributeId
);
1734 aRecordBuilder
.add(IndexType
, inIndexInfo
[anIndex
].IndexType
);
1735 aRecordBuilder
.add(IndexedDataLocation
, inIndexInfo
[anIndex
].IndexedDataLocation
);
1737 // Insert the record into the SchemaIndexes ModifiedTable
1738 anIndexTable
.insertRecord(mVersionId
, &aRecordBuilder
, NULL
);
1740 // update the table's index objects
1741 DbMutableIndex
&index
= aTable
->findIndex(inIndexInfo
[anIndex
].IndexId
,
1742 aTable
->getMetaRecord(), inIndexInfo
[anIndex
].IndexType
== CSSM_DB_INDEX_UNIQUE
);
1743 index
.appendAttribute(inIndexInfo
[anIndex
].AttributeId
);
1748 DbModifier::findTable(Table::Id inTableId
)
1750 ModifiedTableMap::iterator it
= mModifiedTableMap
.find(inTableId
);
1751 if (it
== mModifiedTableMap
.end())
1752 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1758 // AppleDatabaseManager implementation
1761 AppleDatabaseManager::AppleDatabaseManager(const AppleDatabaseTableName
*tableNames
)
1762 : DatabaseManager(),
1763 mTableNames(tableNames
)
1765 // make sure that a proper set of table ids and names has been provided
1768 CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR
);
1771 for (i
= 0; mTableNames
[i
].mTableName
; i
++) {}
1772 if (i
< AppleDatabaseTableName::kNumRequiredTableNames
)
1773 CssmError::throwMe(CSSMERR_DL_INTERNAL_ERROR
);
1778 AppleDatabaseManager::make(const DbName
&inDbName
)
1780 return new AppleDatabase(inDbName
, mTableNames
);
1785 // AppleDbContext implementation
1788 /* This is the version 0 CSSM_APPLEDL_OPEN_PARAMETERS struct used up to 10.2.x. */
1791 typedef struct cssm_appledl_open_parameters_v0
1793 uint32 length
; /* Should be sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0). */
1794 uint32 version
; /* Should be 0. */
1795 CSSM_BOOL autoCommit
;
1796 } CSSM_APPLEDL_OPEN_PARAMETERS_V0
;
1800 AppleDbContext::AppleDbContext(Database
&inDatabase
,
1801 DatabaseSession
&inDatabaseSession
,
1802 CSSM_DB_ACCESS_TYPE inAccessRequest
,
1803 const AccessCredentials
*inAccessCred
,
1804 const void *inOpenParameters
) :
1805 DbContext(inDatabase
, inDatabaseSession
, inAccessRequest
, inAccessCred
),
1809 const CSSM_APPLEDL_OPEN_PARAMETERS
*anOpenParameters
=
1810 reinterpret_cast<const CSSM_APPLEDL_OPEN_PARAMETERS
*>(inOpenParameters
);
1812 if (anOpenParameters
)
1814 switch (anOpenParameters
->version
)
1817 if (anOpenParameters
->length
< sizeof(CSSM_APPLEDL_OPEN_PARAMETERS
))
1818 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS
);
1820 if (anOpenParameters
->mask
& kCSSM_APPLEDL_MASK_MODE
)
1821 mMode
= anOpenParameters
->mode
;
1824 if (anOpenParameters
->length
< sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0
))
1825 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS
);
1827 mAutoCommit
= anOpenParameters
->autoCommit
== CSSM_FALSE
? false : true;
1831 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS
);
1836 AppleDbContext::~AppleDbContext()
1841 // AppleDatabase implementation
1843 AppleDatabase::AppleDatabase(const DbName
&inDbName
, const AppleDatabaseTableName
*tableNames
) :
1845 schemaRelations(tableNames
[AppleDatabaseTableName::kSchemaInfo
].mTableId
,
1846 sizeof(AttrSchemaRelations
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
1847 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaRelations
)),
1848 schemaAttributes(tableNames
[AppleDatabaseTableName::kSchemaAttributes
].mTableId
,
1849 sizeof(AttrSchemaAttributes
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
1850 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaAttributes
)),
1851 schemaIndexes(tableNames
[AppleDatabaseTableName::kSchemaIndexes
].mTableId
,
1852 sizeof(AttrSchemaIndexes
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
1853 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaIndexes
)),
1854 schemaParsingModule(tableNames
[AppleDatabaseTableName::kSchemaParsingModule
].mTableId
,
1855 sizeof(AttrSchemaParsingModule
) / sizeof(CSSM_DB_ATTRIBUTE_INFO
),
1856 const_cast<CSSM_DB_ATTRIBUTE_INFO_PTR
>(AttrSchemaParsingModule
)),
1857 mAtomicFile(mDbName
.dbName()),
1858 mDbModifier(mAtomicFile
, *this),
1859 mTableNames(tableNames
)
1863 AppleDatabase::~AppleDatabase()
1867 // Return the name of a record type. This uses a table that maps record types
1868 // to record names. The table is provided when the database is created.
1870 const char *AppleDatabase::recordName(CSSM_DB_RECORDTYPE inRecordType
) const
1872 if (inRecordType
== CSSM_DL_DB_RECORD_ANY
|| inRecordType
== CSSM_DL_DB_RECORD_ALL_KEYS
)
1873 CssmError::throwMe(CSSMERR_DL_INVALID_RECORDTYPE
);
1875 for (uint32 i
= 0; mTableNames
[i
].mTableName
; i
++)
1876 if (mTableNames
[i
].mTableId
== inRecordType
)
1877 return mTableNames
[i
].mTableName
;
1883 AppleDatabase::makeDbContext(DatabaseSession
&inDatabaseSession
,
1884 CSSM_DB_ACCESS_TYPE inAccessRequest
,
1885 const AccessCredentials
*inAccessCred
,
1886 const void *inOpenParameters
)
1888 return new AppleDbContext(*this, inDatabaseSession
, inAccessRequest
,
1889 inAccessCred
, inOpenParameters
);
1893 AppleDatabase::dbCreate(DbContext
&inDbContext
, const CSSM_DBINFO
&inDBInfo
,
1894 const CSSM_ACL_ENTRY_INPUT
*inInitialAclEntry
)
1896 AppleDbContext
&context
= safer_cast
<AppleDbContext
&>(inDbContext
);
1899 StLock
<Mutex
> _(mWriteLock
);
1900 mDbModifier
.createDatabase(inDBInfo
, inInitialAclEntry
, context
.mode());
1904 mDbModifier
.rollback();
1907 if (context
.autoCommit())
1908 mDbModifier
.commit();
1912 AppleDatabase::dbOpen(DbContext
&inDbContext
)
1914 mDbModifier
.openDatabase();
1918 AppleDatabase::dbClose()
1920 StLock
<Mutex
> _(mWriteLock
);
1921 mDbModifier
.closeDatabase();
1925 AppleDatabase::dbDelete(DatabaseSession
&inDatabaseSession
,
1926 const AccessCredentials
*inAccessCred
)
1928 StLock
<Mutex
> _(mWriteLock
);
1929 // XXX Check callers credentials.
1930 mDbModifier
.deleteDatabase();
1934 AppleDatabase::createRelation(DbContext
&inDbContext
,
1935 CSSM_DB_RECORDTYPE inRelationID
,
1936 const char *inRelationName
,
1937 uint32 inNumberOfAttributes
,
1938 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
&inAttributeInfo
,
1939 uint32 inNumberOfIndexes
,
1940 const CSSM_DB_SCHEMA_INDEX_INFO
&inIndexInfo
)
1944 StLock
<Mutex
> _(mWriteLock
);
1945 // XXX Fix the refs here.
1946 mDbModifier
.insertTable(inRelationID
, inRelationName
,
1947 inNumberOfAttributes
, &inAttributeInfo
,
1948 inNumberOfIndexes
, &inIndexInfo
);
1952 mDbModifier
.rollback();
1955 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
1956 mDbModifier
.commit();
1960 AppleDatabase::destroyRelation(DbContext
&inDbContext
,
1961 CSSM_DB_RECORDTYPE inRelationID
)
1965 StLock
<Mutex
> _(mWriteLock
);
1966 mDbModifier
.deleteTable(inRelationID
);
1970 mDbModifier
.rollback();
1973 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
1974 mDbModifier
.commit();
1978 AppleDatabase::authenticate(DbContext
&inDbContext
,
1979 CSSM_DB_ACCESS_TYPE inAccessRequest
,
1980 const AccessCredentials
&inAccessCred
)
1982 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
1986 AppleDatabase::getDbAcl(DbContext
&inDbContext
,
1987 const CSSM_STRING
*inSelectionTag
,
1988 uint32
&outNumberOfAclInfos
,
1989 CSSM_ACL_ENTRY_INFO_PTR
&outAclInfos
)
1991 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
1995 AppleDatabase::changeDbAcl(DbContext
&inDbContext
,
1996 const AccessCredentials
&inAccessCred
,
1997 const CSSM_ACL_EDIT
&inAclEdit
)
1999 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2003 AppleDatabase::getDbOwner(DbContext
&inDbContext
,
2004 CSSM_ACL_OWNER_PROTOTYPE
&outOwner
)
2006 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2010 AppleDatabase::changeDbOwner(DbContext
&inDbContext
,
2011 const AccessCredentials
&inAccessCred
,
2012 const CSSM_ACL_OWNER_PROTOTYPE
&inNewOwner
)
2014 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2018 AppleDatabase::getDbNameFromHandle(const DbContext
&inDbContext
) const
2020 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
2023 CSSM_DB_UNIQUE_RECORD_PTR
2024 AppleDatabase::dataInsert(DbContext
&inDbContext
,
2025 CSSM_DB_RECORDTYPE inRecordType
,
2026 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributes
,
2027 const CssmData
*inData
)
2029 CSSM_DB_UNIQUE_RECORD_PTR anUniqueRecordPtr
= NULL
;
2032 StLock
<Mutex
> _(mWriteLock
);
2033 const RecordId aRecordId
=
2034 mDbModifier
.insertRecord(inRecordType
, inAttributes
, inData
);
2036 anUniqueRecordPtr
= createUniqueRecord(inDbContext
, inRecordType
,
2038 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2039 mDbModifier
.commit();
2043 if (anUniqueRecordPtr
!= NULL
)
2044 freeUniqueRecord(inDbContext
, *anUniqueRecordPtr
);
2046 mDbModifier
.rollback();
2050 return anUniqueRecordPtr
;
2054 AppleDatabase::dataDelete(DbContext
&inDbContext
,
2055 const CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
)
2059 StLock
<Mutex
> _(mWriteLock
);
2061 const RecordId
aRecordId(parseUniqueRecord(inUniqueRecord
, aTableId
));
2062 mDbModifier
.deleteRecord(aTableId
, aRecordId
);
2066 mDbModifier
.rollback();
2070 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2071 mDbModifier
.commit();
2075 AppleDatabase::dataModify(DbContext
&inDbContext
,
2076 CSSM_DB_RECORDTYPE inRecordType
,
2077 CSSM_DB_UNIQUE_RECORD
&inoutUniqueRecord
,
2078 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*inAttributesToBeModified
,
2079 const CssmData
*inDataToBeModified
,
2080 CSSM_DB_MODIFY_MODE inModifyMode
)
2084 StLock
<Mutex
> _(mWriteLock
);
2086 const RecordId aRecordId
=
2087 mDbModifier
.updateRecord(aTableId
,
2088 parseUniqueRecord(inoutUniqueRecord
, aTableId
),
2089 inAttributesToBeModified
,
2092 updateUniqueRecord(inDbContext
, inRecordType
, aRecordId
, inoutUniqueRecord
);
2096 mDbModifier
.rollback();
2100 if (safer_cast
<AppleDbContext
&>(inDbContext
).autoCommit())
2101 mDbModifier
.commit();
2105 AppleDatabase::dataGetFirst(DbContext
&inDbContext
,
2106 const DLQuery
*inQuery
,
2107 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
2108 CssmData
*inoutData
,
2109 CSSM_DB_UNIQUE_RECORD_PTR
&outUniqueRecord
)
2111 // XXX: register Cursor with DbContext and have DbContext call
2112 // dataAbortQuery for all outstanding Query objects on close.
2113 auto_ptr
<Cursor
> aCursor(mDbModifier
.createCursor(inQuery
));
2117 if (!aCursor
->next(aTableId
, inoutAttributes
, inoutData
,
2118 inDbContext
.mDatabaseSession
, aRecordId
))
2119 // return a NULL handle, and implicitly delete the cursor
2120 return CSSM_INVALID_HANDLE
;
2122 outUniqueRecord
= createUniqueRecord(inDbContext
, aTableId
, aRecordId
);
2123 return aCursor
.release()->handle(); // We didn't throw so keep the Cursor around.
2127 AppleDatabase::dataGetNext(DbContext
&inDbContext
,
2128 CSSM_HANDLE inResultsHandle
,
2129 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
2130 CssmData
*inoutData
,
2131 CSSM_DB_UNIQUE_RECORD_PTR
&outUniqueRecord
)
2133 auto_ptr
<Cursor
> aCursor(&findHandle
<Cursor
>(inResultsHandle
, CSSMERR_DL_INVALID_RESULTS_HANDLE
));
2137 if (!aCursor
->next(aTableId
, inoutAttributes
, inoutData
, inDbContext
.mDatabaseSession
, aRecordId
))
2140 outUniqueRecord
= createUniqueRecord(inDbContext
, aTableId
, aRecordId
);
2147 AppleDatabase::dataAbortQuery(DbContext
&inDbContext
,
2148 CSSM_HANDLE inResultsHandle
)
2150 delete &findHandle
<Cursor
>(inResultsHandle
, CSSMERR_DL_INVALID_RESULTS_HANDLE
);
2154 AppleDatabase::dataGetFromUniqueRecordId(DbContext
&inDbContext
,
2155 const CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
,
2156 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes
,
2157 CssmData
*inoutData
)
2160 const RecordId
aRecordId(parseUniqueRecord(inUniqueRecord
, aTableId
));
2161 // XXX Change CDSA spec to use new RecordId returned by this function
2162 mDbModifier
.getRecord(aTableId
, aRecordId
, inoutAttributes
, inoutData
,
2163 inDbContext
.mDatabaseSession
);
2167 AppleDatabase::freeUniqueRecord(DbContext
&inDbContext
,
2168 CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
)
2170 if (inUniqueRecord
.RecordIdentifier
.Length
!= 0
2171 && inUniqueRecord
.RecordIdentifier
.Data
!= NULL
)
2173 inUniqueRecord
.RecordIdentifier
.Length
= 0;
2174 inDbContext
.mDatabaseSession
.free(inUniqueRecord
.RecordIdentifier
.Data
);
2176 inDbContext
.mDatabaseSession
.free(&inUniqueRecord
);
2180 AppleDatabase::updateUniqueRecord(DbContext
&inDbContext
,
2181 CSSM_DB_RECORDTYPE inTableId
,
2182 const RecordId
&inRecordId
,
2183 CSSM_DB_UNIQUE_RECORD
&inoutUniqueRecord
)
2185 uint32
*aBuffer
= reinterpret_cast<uint32
*>(inoutUniqueRecord
.RecordIdentifier
.Data
);
2186 aBuffer
[0] = inTableId
;
2187 aBuffer
[1] = inRecordId
.mRecordNumber
;
2188 aBuffer
[2] = inRecordId
.mCreateVersion
;
2189 aBuffer
[3] = inRecordId
.mRecordVersion
;
2192 CSSM_DB_UNIQUE_RECORD_PTR
2193 AppleDatabase::createUniqueRecord(DbContext
&inDbContext
,
2194 CSSM_DB_RECORDTYPE inTableId
,
2195 const RecordId
&inRecordId
)
2197 CSSM_DB_UNIQUE_RECORD_PTR aUniqueRecord
=
2198 inDbContext
.mDatabaseSession
.alloc
<CSSM_DB_UNIQUE_RECORD
>();
2199 memset(aUniqueRecord
, 0, sizeof(*aUniqueRecord
));
2200 aUniqueRecord
->RecordIdentifier
.Length
= sizeof(uint32
) * 4;
2203 aUniqueRecord
->RecordIdentifier
.Data
=
2204 inDbContext
.mDatabaseSession
.alloc
<uint8
>(sizeof(uint32
) * 4);
2205 updateUniqueRecord(inDbContext
, inTableId
, inRecordId
, *aUniqueRecord
);
2209 inDbContext
.mDatabaseSession
.free(aUniqueRecord
);
2213 return aUniqueRecord
;
2217 AppleDatabase::parseUniqueRecord(const CSSM_DB_UNIQUE_RECORD
&inUniqueRecord
,
2218 CSSM_DB_RECORDTYPE
&outTableId
)
2220 if (inUniqueRecord
.RecordIdentifier
.Length
!= sizeof(uint32
) * 4)
2221 CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID
);
2223 uint32
*aBuffer
= reinterpret_cast<uint32
*>(inUniqueRecord
.RecordIdentifier
.Data
);
2224 outTableId
= aBuffer
[0];
2225 return RecordId(aBuffer
[1], aBuffer
[2], aBuffer
[3]);
2229 AppleDatabase::passThrough(DbContext
&dbContext
,
2230 uint32 passThroughId
,
2231 const void *inputParams
,
2232 void **outputParams
)
2234 switch (passThroughId
)
2236 case CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
:
2238 CSSM_BOOL on
= reinterpret_cast<CSSM_BOOL
>(inputParams
);
2239 safer_cast
<AppleDbContext
&>(dbContext
).autoCommit(on
);
2243 case CSSM_APPLEFILEDL_COMMIT
:
2244 mDbModifier
.commit();
2247 case CSSM_APPLEFILEDL_ROLLBACK
:
2248 mDbModifier
.rollback();
2252 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);