Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _MDB_
# include "mdb.h"
#endif
#ifndef _MORK_
# include "mork.h"
#endif
#ifndef _MORKNODE_
# include "morkNode.h"
#endif
#ifndef _MORKROW_
# include "morkRow.h"
#endif
#ifndef _MORKENV_
# include "morkEnv.h"
#endif
#ifndef _MORKROWSPACE_
# include "morkRowSpace.h"
#endif
#ifndef _MORKPOOL_
# include "morkPool.h"
#endif
#ifndef _MORKROWOBJECT_
# include "morkRowObject.h"
#endif
#ifndef _MORKCELLOBJECT_
# include "morkCellObject.h"
#endif
#ifndef _MORKCELL_
# include "morkCell.h"
#endif
#ifndef _MORKSTORE_
# include "morkStore.h"
#endif
#ifndef _MORKROWCELLCURSOR_
# include "morkRowCellCursor.h"
#endif
// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
// notifications regarding row changes:
void morkRow::NoteRowAddCol(morkEnv* ev, mork_column inColumn) {
if (!this->IsRowRewrite()) {
mork_delta newDelta;
morkDelta_Init(newDelta, inColumn, morkChange_kAdd);
if (newDelta != mRow_Delta) // not repeating existing data?
{
if (this->HasRowDelta()) // already have one change recorded?
this->SetRowRewrite(); // just plan to write all row cells
else
this->SetRowDelta(inColumn, morkChange_kAdd);
}
} else
this->ClearRowDelta();
}
void morkRow::NoteRowCutCol(morkEnv* ev, mork_column inColumn) {
if (!this->IsRowRewrite()) {
mork_delta newDelta;
morkDelta_Init(newDelta, inColumn, morkChange_kCut);
if (newDelta != mRow_Delta) // not repeating existing data?
{
if (this->HasRowDelta()) // already have one change recorded?
this->SetRowRewrite(); // just plan to write all row cells
else
this->SetRowDelta(inColumn, morkChange_kCut);
}
} else
this->ClearRowDelta();
}
void morkRow::NoteRowSetCol(morkEnv* ev, mork_column inColumn) {
if (!this->IsRowRewrite()) {
if (this->HasRowDelta()) // already have one change recorded?
this->SetRowRewrite(); // just plan to write all row cells
else
this->SetRowDelta(inColumn, morkChange_kSet);
} else
this->ClearRowDelta();
}
void morkRow::NoteRowSetAll(morkEnv* ev) {
this->SetRowRewrite(); // just plan to write all row cells
this->ClearRowDelta();
}
mork_u2 morkRow::AddRowGcUse(morkEnv* ev) {
if (this->IsRow()) {
if (mRow_GcUses < morkRow_kMaxGcUses) // not already maxed out?
++mRow_GcUses;
} else
this->NonRowTypeError(ev);
return mRow_GcUses;
}
mork_u2 morkRow::CutRowGcUse(morkEnv* ev) {
if (this->IsRow()) {
if (mRow_GcUses) // any outstanding uses to cut?
{
if (mRow_GcUses < morkRow_kMaxGcUses) // not frozen at max?
--mRow_GcUses;
} else
this->GcUsesUnderflowWarning(ev);
} else
this->NonRowTypeError(ev);
return mRow_GcUses;
}
/*static*/ void morkRow::GcUsesUnderflowWarning(morkEnv* ev) {
ev->NewWarning("mRow_GcUses underflow");
}
/*static*/ void morkRow::NonRowTypeError(morkEnv* ev) {
ev->NewError("non morkRow");
}
/*static*/ void morkRow::NonRowTypeWarning(morkEnv* ev) {
ev->NewWarning("non morkRow");
}
/*static*/ void morkRow::LengthBeyondMaxError(morkEnv* ev) {
ev->NewError("mRow_Length over max");
}
/*static*/ void morkRow::ZeroColumnError(morkEnv* ev) {
ev->NewError(" zero mork_column");
}
/*static*/ void morkRow::NilCellsError(morkEnv* ev) {
ev->NewError("nil mRow_Cells");
}
void morkRow::InitRow(morkEnv* ev, const mdbOid* inOid, morkRowSpace* ioSpace,
mork_size inLength, morkPool* ioPool)
// if inLength is nonzero, cells will be allocated from ioPool
{
if (ioSpace && ioPool && inOid) {
if (inLength <= morkRow_kMaxLength) {
if (inOid->mOid_Id != morkRow_kMinusOneRid) {
mRow_Space = ioSpace;
mRow_Object = 0;
mRow_Cells = 0;
mRow_Oid = *inOid;
mRow_Length = (mork_u2)inLength;
mRow_Seed = (mork_u2)(mork_ip)this; // "random" assignment
mRow_GcUses = 0;
mRow_Pad = 0;
mRow_Flags = 0;
mRow_Tag = morkRow_kTag;
morkZone* zone = &ioSpace->mSpace_Store->mStore_Zone;
if (inLength) mRow_Cells = ioPool->NewCells(ev, inLength, zone);
if (this->MaybeDirtySpaceStoreAndRow()) // new row might dirty store
{
this->SetRowRewrite();
this->NoteRowSetAll(ev);
}
} else
ioSpace->MinusOneRidError(ev);
} else
this->LengthBeyondMaxError(ev);
} else
ev->NilPointerError();
}
morkRowObject* morkRow::AcquireRowObject(morkEnv* ev, morkStore* ioStore) {
morkRowObject* ro = mRow_Object;
if (ro) // need new row object?
ro->AddRef();
else {
nsIMdbHeap* heap = ioStore->mPort_Heap;
ro = new (*heap, ev)
morkRowObject(ev, morkUsage::kHeap, heap, this, ioStore);
if (!ro) return (morkRowObject*)0;
morkRowObject::SlotWeakRowObject(ro, ev, &mRow_Object);
ro->AddRef();
}
return ro;
}
nsIMdbRow* morkRow::AcquireRowHandle(morkEnv* ev, morkStore* ioStore) {
return AcquireRowObject(ev, ioStore);
}
nsIMdbCell* morkRow::AcquireCellHandle(morkEnv* ev, morkCell* ioCell,
mdb_column inCol, mork_pos inPos) {
nsIMdbHeap* heap = ev->mEnv_Heap;
morkCellObject* cellObj = new (*heap, ev)
morkCellObject(ev, morkUsage::kHeap, heap, this, ioCell, inCol, inPos);
if (cellObj) {
nsIMdbCell* cellHandle = cellObj->AcquireCellHandle(ev);
// cellObj->CutStrongRef(ev->AsMdbEnv());
return cellHandle;
}
return (nsIMdbCell*)0;
}
mork_count morkRow::CountOverlap(morkEnv* ev, morkCell* ioVector,
mork_fill inFill)
// Count cells in ioVector that change existing cells in this row when
// ioVector is added to the row (as in TakeCells()). This is the set
// of cells with the same columns in ioVector and mRow_Cells, which do
// not have exactly the same value in mCell_Atom, and which do not both
// have change status equal to morkChange_kCut (because cutting a cut
// cell still yields a cell that has been cut). CountOverlap() also
// modifies the change attribute of any cell in ioVector to kDup when
// the change was previously kCut and the same column cell was found
// in this row with change also equal to kCut; this tells callers later
// they need not look for that cell in the row again on a second pass.
{
mork_count outCount = 0;
mork_pos pos = 0; // needed by GetCell()
morkCell* cells = ioVector;
morkCell* end = cells + inFill;
--cells; // prepare for preincrement
while (++cells < end && ev->Good()) {
mork_column col = cells->GetColumn();
morkCell* old = this->GetCell(ev, col, &pos);
if (old) // same column?
{
mork_change newChg = cells->GetChange();
mork_change oldChg = old->GetChange();
if (newChg != morkChange_kCut || oldChg != newChg) // not cut+cut?
{
if (cells->mCell_Atom != old->mCell_Atom) // not same atom?
++outCount; // cells will replace old significantly when added
} else
cells->SetColumnAndChange(col, morkChange_kDup); // note dup status
}
}
return outCount;
}
void morkRow::MergeCells(morkEnv* ev, morkCell* ioVector, mork_fill inVecLength,
mork_fill inOldRowFill, mork_fill inOverlap)
// MergeCells() is the part of TakeCells() that does the insertion.
// inOldRowFill is the old value of mRow_Length, and inOverlap is the
// number of cells in the intersection that must be updated.
{
morkCell* newCells = mRow_Cells + inOldRowFill; // 1st new cell in row
morkCell* newEnd = newCells + mRow_Length; // one past last cell
morkCell* srcCells = ioVector;
morkCell* srcEnd = srcCells + inVecLength;
--srcCells; // prepare for preincrement
while (++srcCells < srcEnd && ev->Good()) {
mork_change srcChg = srcCells->GetChange();
if (srcChg != morkChange_kDup) // anything to be done?
{
morkCell* dstCell = 0;
if (inOverlap) {
mork_pos pos = 0; // needed by GetCell()
dstCell = this->GetCell(ev, srcCells->GetColumn(), &pos);
}
if (dstCell) {
--inOverlap; // one fewer intersections to resolve
// swap the atoms in the cells to avoid ref counting here:
morkAtom* dstAtom = dstCell->mCell_Atom;
*dstCell = *srcCells; // bitwise copy, taking src atom
srcCells->mCell_Atom = dstAtom; // forget cell ref, if any
} else if (newCells < newEnd) // another new cell exists?
{
dstCell = newCells++; // alloc another new cell
// take atom from source cell, transferring ref to this row:
*dstCell = *srcCells; // bitwise copy, taking src atom
srcCells->mCell_Atom = 0; // forget cell ref, if any
} else // oops, we ran out...
ev->NewError("out of new cells");
}
}
}
void morkRow::TakeCells(morkEnv* ev, morkCell* ioVector, mork_fill inVecLength,
morkStore* ioStore) {
if (ioVector && inVecLength && ev->Good()) {
++mRow_Seed; // intend to change structure of mRow_Cells
mork_size length = (mork_size)mRow_Length;
mork_count overlap = this->CountOverlap(ev, ioVector, inVecLength);
mork_size growth = inVecLength - overlap; // cells to add
mork_size newLength = length + growth;
if (growth && ev->Good()) // need to add any cells?
{
morkZone* zone = &ioStore->mStore_Zone;
morkPool* pool = ioStore->StorePool();
if (!pool->AddRowCells(ev, this, length + growth, zone))
ev->NewError("cannot take cells");
}
if (ev->Good()) {
if (mRow_Length >= newLength)
this->MergeCells(ev, ioVector, inVecLength, length, overlap);
else
ev->NewError("not enough new cells");
}
}
}
mork_bool morkRow::MaybeDirtySpaceStoreAndRow() {
morkRowSpace* rowSpace = mRow_Space;
if (rowSpace) {
morkStore* store = rowSpace->mSpace_Store;
if (store && store->mStore_CanDirty) {
store->SetStoreDirty();
rowSpace->mSpace_CanDirty = morkBool_kTrue;
}
if (rowSpace->mSpace_CanDirty) {
this->SetRowDirty();
rowSpace->SetRowSpaceDirty();
return morkBool_kTrue;
}
}
return morkBool_kFalse;
}
morkCell* morkRow::NewCell(morkEnv* ev, mdb_column inColumn, mork_pos* outPos,
morkStore* ioStore) {
++mRow_Seed; // intend to change structure of mRow_Cells
mork_size length = (mork_size)mRow_Length;
*outPos = (mork_pos)length;
morkPool* pool = ioStore->StorePool();
morkZone* zone = &ioStore->mStore_Zone;
mork_bool canDirty = this->MaybeDirtySpaceStoreAndRow();
if (pool->AddRowCells(ev, this, length + 1, zone)) {
morkCell* cell = mRow_Cells + length;
// next line equivalent to inline morkCell::SetCellDirty():
if (canDirty)
cell->SetCellColumnDirty(inColumn);
else
cell->SetCellColumnClean(inColumn);
if (canDirty && !this->IsRowRewrite()) this->NoteRowAddCol(ev, inColumn);
return cell;
}
return (morkCell*)0;
}
void morkRow::SeekColumn(morkEnv* ev, mdb_pos inPos, mdb_column* outColumn,
mdbYarn* outYarn) {
morkCell* cells = mRow_Cells;
if (cells && inPos < mRow_Length && inPos >= 0) {
morkCell* c = cells + inPos;
if (outColumn) *outColumn = c->GetColumn();
if (outYarn) morkAtom::GetYarn(c->mCell_Atom, outYarn);
} else {
if (outColumn) *outColumn = 0;
if (outYarn) morkAtom::GetYarn((morkAtom*)0, outYarn);
}
}
void morkRow::NextColumn(morkEnv* ev, mdb_column* ioColumn, mdbYarn* outYarn) {
morkCell* cells = mRow_Cells;
if (cells) {
mork_column last = 0;
mork_column inCol = *ioColumn;
morkCell* end = cells + mRow_Length;
while (cells < end) {
if (inCol == last) // found column?
{
if (outYarn) morkAtom::GetYarn(cells->mCell_Atom, outYarn);
*ioColumn = cells->GetColumn();
return; // stop, we are done
} else {
last = cells->GetColumn();
++cells;
}
}
}
*ioColumn = 0;
if (outYarn) morkAtom::GetYarn((morkAtom*)0, outYarn);
}
morkCell* morkRow::CellAt(morkEnv* ev, mork_pos inPos) const {
MORK_USED_1(ev);
morkCell* cells = mRow_Cells;
if (cells && inPos < mRow_Length && inPos >= 0) {
return cells + inPos;
}
return (morkCell*)0;
}
morkCell* morkRow::GetCell(morkEnv* ev, mdb_column inColumn,
mork_pos* outPos) const {
MORK_USED_1(ev);
morkCell* cells = mRow_Cells;
if (cells) {
morkCell* end = cells + mRow_Length;
while (cells < end) {
mork_column col = cells->GetColumn();
if (col == inColumn) // found the desired column?
{
*outPos = cells - mRow_Cells;
return cells;
} else
++cells;
}
}
*outPos = -1;
return (morkCell*)0;
}
mork_aid morkRow::GetCellAtomAid(morkEnv* ev, mdb_column inColumn) const
// GetCellAtomAid() finds the cell with column inColumn, and sees if the
// atom has a token ID, and returns the atom's ID if there is one. Or
// else zero is returned if there is no such column, or no atom, or if
// the atom has no ID to return. This method is intended to support
// efficient updating of column indexes for rows in a row space.
{
if (this->IsRow()) {
morkCell* cells = mRow_Cells;
if (cells) {
morkCell* end = cells + mRow_Length;
while (cells < end) {
mork_column col = cells->GetColumn();
if (col == inColumn) // found desired column?
{
morkAtom* atom = cells->mCell_Atom;
if (atom && atom->IsBook())
return ((morkBookAtom*)atom)->mBookAtom_Id;
else
return 0;
} else
++cells;
}
}
} else
this->NonRowTypeError(ev);
return 0;
}
void morkRow::EmptyAllCells(morkEnv* ev) {
morkCell* cells = mRow_Cells;
if (cells) {
morkStore* store = this->GetRowSpaceStore(ev);
if (store) {
if (this->MaybeDirtySpaceStoreAndRow()) {
this->SetRowRewrite();
this->NoteRowSetAll(ev);
}
morkPool* pool = store->StorePool();
morkCell* end = cells + mRow_Length;
--cells; // prepare for preincrement:
while (++cells < end) {
if (cells->mCell_Atom) cells->SetAtom(ev, (morkAtom*)0, pool);
}
}
}
}
void morkRow::cut_all_index_entries(morkEnv* ev) {
morkRowSpace* rowSpace = mRow_Space;
if (rowSpace->mRowSpace_IndexCount) // any indexes?
{
morkCell* cells = mRow_Cells;
if (cells) {
morkCell* end = cells + mRow_Length;
--cells; // prepare for preincrement:
while (++cells < end) {
morkAtom* atom = cells->mCell_Atom;
if (atom) {
mork_aid atomAid = atom->GetBookAtomAid();
if (atomAid) {
mork_column col = cells->GetColumn();
morkAtomRowMap* map = rowSpace->FindMap(ev, col);
if (map) // cut row from index for this column?
map->CutAid(ev, atomAid);
}
}
}
}
}
}
void morkRow::CutAllColumns(morkEnv* ev) {
morkStore* store = this->GetRowSpaceStore(ev);
if (store) {
if (this->MaybeDirtySpaceStoreAndRow()) {
this->SetRowRewrite();
this->NoteRowSetAll(ev);
}
morkRowSpace* rowSpace = mRow_Space;
if (rowSpace->mRowSpace_IndexCount) // any indexes?
this->cut_all_index_entries(ev);
morkPool* pool = store->StorePool();
pool->CutRowCells(ev, this, /*newSize*/ 0, &store->mStore_Zone);
}
}
void morkRow::SetRow(morkEnv* ev, const morkRow* inSourceRow) {
// note inSourceRow might be in another DB, with a different store...
morkStore* store = this->GetRowSpaceStore(ev);
morkStore* srcStore = inSourceRow->GetRowSpaceStore(ev);
if (store && srcStore) {
if (this->MaybeDirtySpaceStoreAndRow()) {
this->SetRowRewrite();
this->NoteRowSetAll(ev);
}
morkRowSpace* rowSpace = mRow_Space;
mork_count indexes = rowSpace->mRowSpace_IndexCount; // any indexes?
mork_bool sameStore = (store == srcStore); // identical stores?
morkPool* pool = store->StorePool();
if (pool->CutRowCells(ev, this, /*newSize*/ 0, &store->mStore_Zone)) {
mork_fill fill = inSourceRow->mRow_Length;
if (pool->AddRowCells(ev, this, fill, &store->mStore_Zone)) {
morkCell* dst = mRow_Cells;
morkCell* dstEnd = dst + mRow_Length;
const morkCell* src = inSourceRow->mRow_Cells;
const morkCell* srcEnd = src + fill;
--dst;
--src; // prepare both for preincrement:
while (++dst < dstEnd && ++src < srcEnd && ev->Good()) {
morkAtom* atom = src->mCell_Atom;
mork_column dstCol = src->GetColumn();
// Note we modify the mCell_Atom slot directly instead of using
// morkCell::SetAtom(), because we know it starts equal to nil.
if (sameStore) // source and dest in same store?
{
// next line equivalent to inline morkCell::SetCellDirty():
dst->SetCellColumnDirty(dstCol);
dst->mCell_Atom = atom;
if (atom) // another ref to non-nil atom?
atom->AddCellUse(ev);
} else // need to dup items from src store in a dest store
{
dstCol = store->CopyToken(ev, dstCol, srcStore);
if (dstCol) {
// next line equivalent to inline morkCell::SetCellDirty():
dst->SetCellColumnDirty(dstCol);
atom = store->CopyAtom(ev, atom);
dst->mCell_Atom = atom;
if (atom) // another ref?
atom->AddCellUse(ev);
}
}
if (indexes && atom) {
mork_aid atomAid = atom->GetBookAtomAid();
if (atomAid) {
morkAtomRowMap* map = rowSpace->FindMap(ev, dstCol);
if (map) map->AddAid(ev, atomAid, this);
}
}
}
}
}
}
}
void morkRow::AddRow(morkEnv* ev, const morkRow* inSourceRow) {
if (mRow_Length) // any existing cells we might need to keep?
{
ev->StubMethodOnlyError();
} else
this->SetRow(ev, inSourceRow); // just exactly duplicate inSourceRow
}
void morkRow::OnZeroRowGcUse(morkEnv* ev)
// OnZeroRowGcUse() is called when CutRowGcUse() returns zero.
{
MORK_USED_1(ev);
// ev->NewWarning("need to implement OnZeroRowGcUse");
}
void morkRow::DirtyAllRowContent(morkEnv* ev) {
MORK_USED_1(ev);
if (this->MaybeDirtySpaceStoreAndRow()) {
this->SetRowRewrite();
this->NoteRowSetAll(ev);
}
morkCell* cells = mRow_Cells;
if (cells) {
morkCell* end = cells + mRow_Length;
--cells; // prepare for preincrement:
while (++cells < end) {
cells->SetCellDirty();
}
}
}
morkStore* morkRow::GetRowSpaceStore(morkEnv* ev) const {
morkRowSpace* rowSpace = mRow_Space;
if (rowSpace) {
morkStore* store = rowSpace->mSpace_Store;
if (store) {
if (store->IsStore()) {
return store;
} else
store->NonStoreTypeError(ev);
} else
ev->NilPointerError();
} else
ev->NilPointerError();
return (morkStore*)0;
}
void morkRow::CutColumn(morkEnv* ev, mdb_column inColumn) {
mork_pos pos = -1;
morkCell* cell = this->GetCell(ev, inColumn, &pos);
if (cell) {
morkStore* store = this->GetRowSpaceStore(ev);
if (store) {
if (this->MaybeDirtySpaceStoreAndRow() && !this->IsRowRewrite())
this->NoteRowCutCol(ev, inColumn);
morkRowSpace* rowSpace = mRow_Space;
morkAtomRowMap* map = (rowSpace->mRowSpace_IndexCount)
? rowSpace->FindMap(ev, inColumn)
: (morkAtomRowMap*)0;
if (map) // this row attribute is indexed by row space?
{
morkAtom* oldAtom = cell->mCell_Atom;
if (oldAtom) // need to cut an entry from the index?
{
mork_aid oldAid = oldAtom->GetBookAtomAid();
if (oldAid) // cut old row attribute from row index in space?
map->CutAid(ev, oldAid);
}
}
morkPool* pool = store->StorePool();
cell->SetAtom(ev, (morkAtom*)0, pool);
mork_fill fill = mRow_Length; // should not be zero
MORK_ASSERT(fill);
if (fill) // index < fill for last cell exists?
{
mork_fill last = fill - 1; // index of last cell in row
if (pos < (mork_pos)last) // need to move cells following cut cell?
{
morkCell* lastCell = mRow_Cells + last;
mork_count after = last - pos; // cell count after cut cell
morkCell* next = cell + 1; // next cell after cut cell
MORK_MEMMOVE(cell, next, after * sizeof(morkCell));
lastCell->SetColumnAndChange(0, 0);
lastCell->mCell_Atom = 0;
}
if (ev->Good())
pool->CutRowCells(ev, this, fill - 1, &store->mStore_Zone);
}
}
}
}
morkAtom* morkRow::GetColumnAtom(morkEnv* ev, mdb_column inColumn) {
if (ev->Good()) {
mork_pos pos = -1;
morkCell* cell = this->GetCell(ev, inColumn, &pos);
if (cell) return cell->mCell_Atom;
}
return (morkAtom*)0;
}
void morkRow::AddColumn(morkEnv* ev, mdb_column inColumn, const mdbYarn* inYarn,
morkStore* ioStore) {
if (ev->Good()) {
mork_pos pos = -1;
morkCell* cell = this->GetCell(ev, inColumn, &pos);
morkCell* oldCell = cell; // need to know later whether new
if (!cell) // column does not yet exist?
cell = this->NewCell(ev, inColumn, &pos, ioStore);
if (cell) {
morkAtom* oldAtom = cell->mCell_Atom;
morkAtom* atom = ioStore->YarnToAtom(ev, inYarn, true /* create */);
if (atom && atom != oldAtom) {
morkRowSpace* rowSpace = mRow_Space;
morkAtomRowMap* map = (rowSpace->mRowSpace_IndexCount)
? rowSpace->FindMap(ev, inColumn)
: (morkAtomRowMap*)0;
if (map) // inColumn is indexed by row space?
{
if (oldAtom && oldAtom != atom) // cut old cell from index?
{
mork_aid oldAid = oldAtom->GetBookAtomAid();
if (oldAid) // cut old row attribute from row index in space?
map->CutAid(ev, oldAid);
}
}
cell->SetAtom(ev, atom, ioStore->StorePool()); // refcounts atom
if (oldCell) // we changed a pre-existing cell in the row?
{
++mRow_Seed;
if (this->MaybeDirtySpaceStoreAndRow() && !this->IsRowRewrite())
this->NoteRowAddCol(ev, inColumn);
}
if (map) // inColumn is indexed by row space?
{
mork_aid newAid = atom->GetBookAtomAid();
if (newAid) // add new row attribute to row index in space?
map->AddAid(ev, newAid, this);
}
}
}
}
}
morkRowCellCursor* morkRow::NewRowCellCursor(morkEnv* ev, mdb_pos inPos) {
morkRowCellCursor* outCursor = 0;
if (ev->Good()) {
morkStore* store = this->GetRowSpaceStore(ev);
if (store) {
morkRowObject* rowObj = this->AcquireRowObject(ev, store);
if (rowObj) {
nsIMdbHeap* heap = store->mPort_Heap;
morkRowCellCursor* cursor = new (*heap, ev)
morkRowCellCursor(ev, morkUsage::kHeap, heap, rowObj);
if (cursor) {
if (ev->Good()) {
cursor->mRowCellCursor_Col = inPos;
outCursor = cursor;
} else
cursor->CutStrongRef(ev->mEnv_SelfAsMdbEnv);
}
rowObj->Release(); // always cut ref (cursor has its own)
}
}
}
return outCursor;
}
// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789