3411 lines
80 KiB
C++
3411 lines
80 KiB
C++
/*
|
|
----
|
|
This file is part of SECONDO.
|
|
|
|
Copyright (C) 2004-2009, University in Hagen, Faculty of Mathematics
|
|
and Computer Science, Database Systems for New Applications.
|
|
|
|
SECONDO is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
SECONDO is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with SECONDO; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
----
|
|
|
|
//paragraph [1] Title: [{\Large \bf \begin{center}] [\end{center}}]
|
|
//paragraph [10] Footnote: [{\footnote{] [}}]
|
|
//[TOC] [\tableofcontents]
|
|
|
|
[1] Implementation of Module Relation Algebra
|
|
|
|
[1] Separate part of persistent data representation
|
|
|
|
June 1996 Claudia Freundorfer
|
|
|
|
May 2002 Frank Hoffmann port to C++
|
|
|
|
November 7, 2002 RHG Corrected the type mapping of ~tcount~.
|
|
|
|
November 30, 2002 RHG Introduced a function ~RelPersistValue~
|
|
instead of ~DefaultPersistValue~ which keeps relations that have
|
|
been built in memory in a small cache, so that they need not be
|
|
rebuilt from then on.
|
|
|
|
March 2003 Victor Almeida created the new Relational Algebra
|
|
organization
|
|
|
|
November 2004 M. Spiekermann. The declarations of the
|
|
PrivateRelation have been moved to the files RelationPersistent.h
|
|
and RelationMainMemory.h. This was necessary to implement some
|
|
little functions as inline functions.
|
|
|
|
June 2005 M. Spiekermann. The tuple's size information will now be i
|
|
stored in member variables and only recomputed after attributes
|
|
were changed. Changes in class ~TupleBuffer~ which allow to store
|
|
tuples as "free" or "non-free" tuples in it.
|
|
|
|
December 2005, Victor Almeida deleted the deprecated algebra levels
|
|
(~executable~, ~descriptive~, and ~hibrid~). Only the executable
|
|
level remains. Models are also removed from type constructors.
|
|
|
|
January 2006 Victor Almeida replaced the ~free~ tuples concept to
|
|
reference counters. There are reference counters on tuples and also
|
|
on attributes. Some assertions were removed, since the code is
|
|
stable.
|
|
|
|
April 2006, M. Spiekermann. Introduction of a new function ~clearAll~ in class
|
|
~PrivateTupleBuffer~. This function calls the tuples' member functions
|
|
~DecReference~ and ~DeleteIfAllowed~ instead of deleting the tuple pointer
|
|
directly and is called by the destructor.
|
|
|
|
January 2007, M. Spiekermann. A memory leak in ~PrivateTupleBuffer~ has been
|
|
fixed.
|
|
|
|
April 2007, T Behr. The concept of solid tuples has been removed.
|
|
|
|
May 2007, M. Spiekermann. From now on the function TupleBuffer::Clear()
|
|
deletes a the pointer to a relation object and marks the buffer's state
|
|
as memory only.
|
|
|
|
June 2009, S.Jungnickel. Added implementation for classes ~TupleFile~ and
|
|
~TupleFileIterator~. Added implementation for new methods ~Save~ and ~Open~
|
|
of class ~Tuple~.
|
|
|
|
October 2009, S.Jungnickel. Constructor of TupleFileIterator now rewinds
|
|
read/write position of a TupleFile. Solved some problems when temporary closing
|
|
and reopening a ~TupleFile~.
|
|
|
|
|
|
[TOC]
|
|
|
|
1 Overview
|
|
|
|
The Relational Algebra basically implements two type constructors,
|
|
namely ~tuple~ and ~rel~.
|
|
|
|
More information about the Relational Algebra can be found in the
|
|
RelationAlgebra.h header file.
|
|
|
|
This file contains the implementation of the Persistent Relational
|
|
Algebra, where the type constructors ~tuple~ and ~rel~ are kept in
|
|
secondary memory.
|
|
|
|
A relation has two files: the tuple file and the LOB file, for
|
|
storing tuples and LOBs respectively.
|
|
|
|
|
|
2 Includes, Constants, Globals, Enumerations
|
|
|
|
*/
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include "RelationAlgebra.h"
|
|
#include "StringUtils.h"
|
|
|
|
#include "LogMsg.h"
|
|
#include "QueryProcessor.h"
|
|
#include "NestedList.h"
|
|
#include "SecondoSystem.h"
|
|
#include "SecondoSMI.h"
|
|
#include "../Tools/Flob/Flob.h"
|
|
//#include "FLOBCache.h"
|
|
#include "WinUnix.h"
|
|
#include "Symbols.h"
|
|
#include "StandardTypes.h"
|
|
#include "Serialize.h"
|
|
#include "FileSystem.h"
|
|
|
|
#include "Base64.h"
|
|
|
|
using namespace std;
|
|
|
|
extern NestedList *nl;
|
|
extern QueryProcessor *qp;
|
|
extern AlgebraManager *am;
|
|
|
|
|
|
static Base64 B64;
|
|
|
|
|
|
/*
|
|
Output funtions
|
|
|
|
*/
|
|
ostream& operator<<(ostream& o, AttributeType& at){
|
|
o << "[ algId =" << at.algId
|
|
<< ", typeId =" << at.typeId
|
|
<< ", size =" << at.size
|
|
<< ", offset =" << at.offset
|
|
<< "]";
|
|
return o;
|
|
}
|
|
|
|
ostream& operator<<(ostream& o, const TupleType& tt){
|
|
o << " noAttributes " << tt.noAttributes
|
|
<< " totalSize " << tt.totalSize
|
|
<< " coreSize " << tt.coreSize
|
|
<< " refs " << tt.refs
|
|
<< " AttrTypes ";
|
|
for(int i=0 ; i< tt.noAttributes; i++){
|
|
o << "[" << i << "]" << tt.attrTypeArray[i] << " , ";
|
|
}
|
|
return o;
|
|
}
|
|
|
|
ostream& operator<<(ostream& o, const RelationDescriptor& rd){
|
|
o << "[ " "tupleType " << (*rd.tupleType) << ", "
|
|
<< "noTuples " << rd.noTuples << ", "
|
|
<< "totalExtSize " << rd.totalExtSize<< ", "
|
|
<< "totalSize " << rd.totalSize << ", "
|
|
<< "tupleFileId " << rd.tupleFileId << ", "
|
|
<< "lobFileId " << rd.lobFileId << ", ";
|
|
|
|
o << "attrExtSize [";
|
|
for(unsigned int i=0; i< rd.attrExtSize.size(); i++){
|
|
o << rd.attrExtSize[i] << ", ";
|
|
}
|
|
o << "]";
|
|
|
|
o << "attrSize [";
|
|
for(unsigned int i=0; i< rd.attrSize.size(); i++){
|
|
o << rd.attrSize[i] << ", ";
|
|
}
|
|
o << "]";
|
|
|
|
o << "]";
|
|
|
|
return o;
|
|
}
|
|
|
|
|
|
|
|
char Tuple::tupleData[MAX_TUPLESIZE];
|
|
|
|
void
|
|
Attribute::counters(bool reset, bool show)
|
|
{
|
|
StdTypes::UpdateBasicOps(reset);
|
|
|
|
Counter::reportValue(Symbol::CTR_ATTR_BASIC_OPS(), show);
|
|
Counter::reportValue(Symbol::CTR_ATTR_HASH_OPS(), show);
|
|
Counter::reportValue(Symbol::CTR_ATTR_COMPARE_OPS(), show);
|
|
}
|
|
|
|
void
|
|
Attribute::InitCounters(bool show) {
|
|
counters(true, show);
|
|
}
|
|
|
|
void
|
|
Attribute::SetCounterValues(bool show) {
|
|
counters(false, show);
|
|
}
|
|
|
|
|
|
/*
|
|
3 Type constructor ~tuple~
|
|
|
|
3.2 Struct ~PrivateTuple~
|
|
|
|
This struct contains the private attributes of the class ~Tuple~.
|
|
|
|
*/
|
|
|
|
Tuple::~Tuple()
|
|
{
|
|
DEBUG_MSG("Destructor called.")
|
|
// delete all attributes if no further references exist
|
|
for( size_t i = 0; i < noAttributes; i++ ){
|
|
if( attributes[i] != 0 )
|
|
{
|
|
DEBUG_MSG("call attributes[" << i << "]->DeleteIfAllowed() with"
|
|
<< " del.refs = " << (int)attributes[i]->del.refs)
|
|
attributes[i]->DeleteIfAllowed();
|
|
attributes[i] = 0;
|
|
}
|
|
else {
|
|
DEBUG_MSG("attributes[" << i << "] == 0")
|
|
}
|
|
}
|
|
if(noAttributes > MAX_NUM_OF_ATTR){
|
|
delete[] attributes;
|
|
}
|
|
attributes=0;
|
|
tupleType->DeleteIfAllowed();
|
|
tupleType = 0;
|
|
// do not delete the tuple file
|
|
|
|
tuplesDeleted++;
|
|
tuplesInMemory--;
|
|
}
|
|
/*
|
|
The destructor.
|
|
|
|
*/
|
|
|
|
|
|
void Tuple::Save( SmiRecordFile* file,
|
|
const SmiFileId& fid,
|
|
double& extSize, double& size,
|
|
vector<double>& attrExtSize, vector<double>& attrSize,
|
|
const bool ignoreLobs /*=false*/)
|
|
{
|
|
TRACE_ENTER
|
|
|
|
this->tupleFile = file;
|
|
this->lobFileId = fid;
|
|
|
|
#ifdef TRACE_ON
|
|
static long& saveCtr = Counter::getRef("RA:Tuple::Save");
|
|
saveCtr++;
|
|
#endif
|
|
|
|
// Calculate the size of the small FLOB data which will be
|
|
// saved together with the tuple attributes and save the LOBs
|
|
// in the lobFile.
|
|
extSize += tupleType->GetCoreSize();
|
|
size += tupleType->GetCoreSize();
|
|
|
|
size_t coreSize = 0;
|
|
|
|
// create a new record for the tuple
|
|
DEBUG_MSG("Appending tuple record!")
|
|
SmiRecord *tupleRecord = new SmiRecord();
|
|
// SmiRecordId tupleId;
|
|
bool rc = file->AppendRecord( tupleId, *tupleRecord );
|
|
assert(rc == true);
|
|
|
|
size_t extensionSize
|
|
= CalculateBlockSize( coreSize, extSize,
|
|
size, attrExtSize,
|
|
attrSize);
|
|
|
|
char* data = WriteToBlock(coreSize, extensionSize, ignoreLobs, file, fid);
|
|
|
|
// Write data into the record
|
|
DEBUG_MSG("Writing tuple record!")
|
|
SHOW(coreSize)
|
|
SHOW(extensionSize)
|
|
|
|
uint16_t rootSize = coreSize + extensionSize;
|
|
const size_t rootSizeLen = sizeof(rootSize);
|
|
rc = tupleRecord->Write(data, rootSizeLen + rootSize, 0);
|
|
assert(rc == true);
|
|
|
|
// free allocated resources
|
|
tupleRecord->Finish();
|
|
delete tupleRecord;
|
|
|
|
free(data);
|
|
TRACE_LEAVE
|
|
}
|
|
|
|
|
|
void Tuple::Save(TupleFile& tuplefile)
|
|
{
|
|
PinAttributes();
|
|
double extSize = 0;
|
|
double size = 0;
|
|
vector<double> attrExtSize(tupleType->GetNoAttributes());
|
|
vector<double> attrSize(tupleType->GetNoAttributes());
|
|
|
|
// Calculate the size of the small FLOB data which will be
|
|
// saved together with the tuple attributes and save the LOBs
|
|
// in the lobFile.
|
|
extSize += tupleType->GetCoreSize();
|
|
size += tupleType->GetCoreSize();
|
|
size_t coreSize = 0;
|
|
size_t extensionSize = CalculateBlockSize( coreSize, extSize,
|
|
size, attrExtSize,
|
|
attrSize);
|
|
|
|
// Put core and extension part into a single memory block
|
|
// Note: this memory block contains already the block size
|
|
// as uint16_t value
|
|
char* data = WriteToBlock(coreSize, extensionSize, true, 0,0);
|
|
|
|
|
|
// Append data to temporary tuple file
|
|
tuplefile.Append(data, coreSize, extensionSize);
|
|
|
|
// Free the allocated memory block
|
|
free(data);
|
|
}
|
|
|
|
|
|
/*
|
|
~UpdateSave~ saves updated tuple data to disk.
|
|
|
|
*/
|
|
void Tuple::UpdateSave( const vector<int>& changedIndices,
|
|
double& extSize, double& size,
|
|
vector<double>& attrExtSize,
|
|
vector<double>& attrSize )
|
|
{
|
|
size_t attrSizes = 0;
|
|
size_t extensionSize
|
|
= CalculateBlockSize( attrSizes, extSize,
|
|
size, attrExtSize, attrSize);
|
|
|
|
char* data = WriteToBlock( attrSizes, extensionSize, false,
|
|
tupleFile, lobFileId );
|
|
|
|
SmiRecord *tupleRecord = new SmiRecord();
|
|
bool ok = tupleFile->SelectRecord( tupleId, *tupleRecord,
|
|
SmiFile::Update );
|
|
if (!ok)
|
|
{
|
|
cerr << "Fatal Error: Record for updated tuple not found!" << endl;
|
|
cerr << "tupleId = " << tupleId << endl;
|
|
assert (false);
|
|
}
|
|
const size_t oldRecordSize = tupleRecord->Size();
|
|
|
|
uint16_t rootSize = attrSizes + extensionSize;
|
|
const size_t rootSizeLen = sizeof(rootSize);
|
|
const size_t bufSize = rootSizeLen + rootSize;
|
|
|
|
SHOW(rootSize)
|
|
SHOW(bufSize)
|
|
SHOW(oldRecordSize)
|
|
|
|
// The record must be truncated if its size decreased.
|
|
if( bufSize < oldRecordSize ){
|
|
tupleRecord->Truncate( bufSize );
|
|
}
|
|
|
|
// write the new data
|
|
SmiSize len = 0;
|
|
len = tupleRecord->Write(data, bufSize);
|
|
assert(len == bufSize);
|
|
|
|
tupleRecord->Finish();
|
|
delete tupleRecord;
|
|
|
|
free( data );
|
|
}
|
|
|
|
|
|
|
|
char* Tuple::WriteToBlock( size_t coreSize,
|
|
size_t extensionSize,
|
|
bool ignoreLobs,
|
|
SmiRecordFile* tuplefile,
|
|
const SmiFileId& lobFileId) const
|
|
{
|
|
TRACE_ENTER
|
|
// create a single block able to pick up the roots of the
|
|
// attributes and all small FLOBs
|
|
|
|
const uint16_t dataSize = coreSize + extensionSize;
|
|
|
|
const size_t recordSizeLen = sizeof(dataSize);
|
|
const size_t blockSize = dataSize + recordSizeLen;
|
|
|
|
|
|
char* data = (char*) malloc( blockSize );
|
|
|
|
WriteToBlock(data, coreSize, extensionSize,
|
|
ignoreLobs, tuplefile, lobFileId);
|
|
|
|
return data;
|
|
}
|
|
|
|
void Tuple::WriteToBlock(char* buf,
|
|
size_t coreSize,
|
|
size_t extensionSize,
|
|
bool ignoreLobs,
|
|
SmiRecordFile* file,
|
|
const SmiFileId& lobFileId,
|
|
bool containLOBs/* = false*/) const
|
|
{
|
|
TRACE_ENTER
|
|
assert(buf);
|
|
|
|
const uint16_t dataSize = coreSize + extensionSize;
|
|
|
|
char* data = buf;
|
|
char* ext = data + sizeof(dataSize) + coreSize;
|
|
|
|
char* lob = 0;
|
|
SmiSize lobOffset = 0;
|
|
if (containLOBs)
|
|
{
|
|
lobOffset = sizeof(dataSize) + coreSize + extensionSize;
|
|
lob = data + lobOffset;
|
|
}
|
|
|
|
// current position in the core part
|
|
SmiSize offset = 0;
|
|
WriteVar<uint16_t>( dataSize, data, offset );
|
|
|
|
// current position in the extension
|
|
SmiSize extOffset = offset + coreSize;
|
|
|
|
SHOW(offset)
|
|
SHOW(extOffset)
|
|
|
|
uint32_t currentSize = 0;
|
|
uint32_t currentExtSize = 0;
|
|
|
|
// collect all attributes into the memory block
|
|
for( size_t i = 0; i < noAttributes; i++)
|
|
{
|
|
SHOW(attributes[i]->IsDefined())
|
|
|
|
Attribute::StorageType st = attributes[i]->GetStorageType();
|
|
|
|
if (Attribute::Default == st)
|
|
{
|
|
//Write Flob data and adjust FlobIds if necessary
|
|
|
|
// vector to remember old flob states
|
|
vector<Flob> attrFlobs;
|
|
|
|
for (int j = 0; j < attributes[i]->NumOfFLOBs(); j++)
|
|
{
|
|
Flob *tmpFlob = attributes[i]->GetFLOB(j);
|
|
SmiSize flobsz = tmpFlob->getSize();
|
|
attrFlobs.push_back(*tmpFlob);
|
|
|
|
if(!ignoreLobs && (flobsz >= extensionLimit))
|
|
{
|
|
if (!containLOBs)
|
|
{
|
|
DEBUG_MSG("tmpFlob->saveToFile");
|
|
tmpFlob->saveToFile( lobFileId,(char)0, *tmpFlob );
|
|
}
|
|
else
|
|
{
|
|
//Write big Flobs to the flob area,
|
|
//located in the end of the memory block
|
|
assert(lob);
|
|
|
|
// write data to flob area
|
|
tmpFlob->read(lob, flobsz);
|
|
|
|
SmiFileId fid(0);
|
|
bool isTemp = true;
|
|
if(tupleFile){
|
|
fid = tupleFile->GetFileId();
|
|
isTemp = tupleFile->IsTemp();
|
|
}
|
|
char mode = isTemp?1:0;
|
|
Flob newFlob = Flob::createFrom(fid, tupleId,
|
|
lobOffset, mode, flobsz);
|
|
|
|
// change flob header
|
|
*tmpFlob = newFlob;
|
|
|
|
// update lob offset
|
|
lobOffset += flobsz;
|
|
lob += flobsz;
|
|
}
|
|
}
|
|
else if (flobsz < extensionLimit)
|
|
{ // put small flobs to memory block too
|
|
|
|
// write data to extension
|
|
tmpFlob->read(ext, flobsz);
|
|
|
|
SmiFileId fid(0);
|
|
bool isTemp = true;
|
|
if(tupleFile){
|
|
fid = tupleFile->GetFileId();
|
|
isTemp = tupleFile->IsTemp();
|
|
}
|
|
char mode = isTemp?1:0;
|
|
Flob newFlob = Flob::createFrom( fid, tupleId,
|
|
extOffset, mode, flobsz );
|
|
|
|
// change flob header
|
|
*tmpFlob = newFlob;
|
|
|
|
// update ext offset
|
|
extOffset += flobsz;
|
|
ext += flobsz;
|
|
}
|
|
|
|
SHOW(extOffset)
|
|
}
|
|
|
|
//Write attribute data
|
|
currentSize = tupleType->GetAttributeType(i).size;
|
|
SHOW(currentSize)
|
|
|
|
DEBUG_MSG( Array2HexStr( (char*)attributes[i], currentSize) )
|
|
attributes[i]->Serialize( data, currentSize, offset );
|
|
offset += currentSize;
|
|
|
|
// restore old flob data
|
|
assert((size_t)attributes[i]->NumOfFLOBs() == attrFlobs.size());
|
|
for( int j = 0; j < attributes[i]->NumOfFLOBs(); j++) {
|
|
Flob *tmpFlob = attributes[i]->GetFLOB(j);
|
|
*tmpFlob = attrFlobs[j];
|
|
}
|
|
attrFlobs.clear();
|
|
}
|
|
else if (Attribute::Core == st)
|
|
{
|
|
assert( attributes[i]->NumOfFLOBs() == 0 );
|
|
|
|
currentSize = attributes[i]->SerializedSize();
|
|
attributes[i]->Serialize( data, currentSize, offset );
|
|
offset += currentSize;
|
|
}
|
|
else if (Attribute::Extension == st)
|
|
{
|
|
assert( attributes[i]->NumOfFLOBs() == 0 );
|
|
|
|
currentSize = sizeof(uint32_t);
|
|
|
|
WriteVar<uint32_t>( extOffset, data, offset );
|
|
SHOW(extOffset)
|
|
|
|
currentExtSize = attributes[i]->SerializedSize();
|
|
attributes[i]->Serialize(ext, currentExtSize, 0);
|
|
DEBUG_MSG( Array2HexStr(ext, currentExtSize) )
|
|
|
|
extOffset += currentExtSize;
|
|
ext += currentExtSize;
|
|
}
|
|
else
|
|
{
|
|
cerr << "ERROR: unknown storage type for attribute No " << i << endl;
|
|
assert(false);
|
|
}
|
|
|
|
SHOW(currentSize)
|
|
SHOW(currentExtSize)
|
|
|
|
SHOW(offset)
|
|
SHOW(extOffset)
|
|
}
|
|
|
|
SHOW((void*)data)
|
|
DEBUG_MSG( Array2HexStr(data, blockSize) )
|
|
|
|
TRACE_LEAVE
|
|
}
|
|
|
|
void Tuple::WriteToDivBlock(char* buf, size_t coreSize,
|
|
size_t extensionSize, size_t flobSize,
|
|
SmiFileId flobFileId, SmiRecordId sourceDS, SmiSize& flobBlockOffset,
|
|
map<pair<SmiFileId, SmiRecordId>, SmiSize>& flobIdCache) const
|
|
{
|
|
TRACE_ENTER
|
|
assert(buf);
|
|
|
|
//Write block size (uint32)
|
|
const uint16_t dataSize = coreSize + extensionSize;
|
|
uint32_t blockSize = sizeof(uint32_t) + sizeof(u_int16_t) + dataSize;
|
|
|
|
SmiSize offset = 0;
|
|
WriteVar<uint32_t>(blockSize, buf, offset);
|
|
char* data = buf + offset;
|
|
|
|
//Write tuple size (uint16)
|
|
offset = 0;
|
|
WriteVar<uint16_t>(dataSize, data, offset);
|
|
|
|
SmiSize extOffset = sizeof(dataSize) + coreSize;
|
|
char* ext = data + extOffset;
|
|
//The flob offset within the current block
|
|
SmiSize lobOffset = extOffset + extensionSize;
|
|
char* lob = data + lobOffset;
|
|
|
|
uint32_t currentSize = 0;
|
|
uint32_t currentExtSize = 0;
|
|
char mode = 3;
|
|
char smode = 1; //mode for small Flob data
|
|
SmiRecordId rcdId = sourceDS;
|
|
|
|
for (size_t i = 0; i < noAttributes; i++)
|
|
{
|
|
SHOW(attributes[i]->IsDefined())
|
|
|
|
Attribute::StorageType st = attributes[i]->GetStorageType();
|
|
|
|
if (Attribute::Default == st)
|
|
{
|
|
vector<Flob> attrFlobs;
|
|
|
|
for (int j = 0; j < attributes[i]->NumOfFLOBs(); j++)
|
|
{
|
|
Flob *tmpFlob = attributes[i]->GetFLOB(j);
|
|
SmiSize flobsz = tmpFlob->getSize();
|
|
attrFlobs.push_back(*tmpFlob);
|
|
|
|
if (flobsz >= extensionLimit)
|
|
{
|
|
// put big flobs to the flob part
|
|
tmpFlob->read(lob, flobsz);
|
|
|
|
SmiSize curFlobOffset = flobBlockOffset;
|
|
bool writeCurrentFlob = true;
|
|
if (tmpFlob->getMode() == 0){
|
|
map<pair<SmiFileId, SmiRecordId>, SmiSize>::iterator lobId
|
|
= flobIdCache.find(
|
|
make_pair(tmpFlob->getFileId(), tmpFlob->getRecordId()));
|
|
|
|
if (lobId != flobIdCache.end()){
|
|
curFlobOffset = lobId->second;
|
|
writeCurrentFlob = false;
|
|
} else {
|
|
flobIdCache.insert(make_pair(
|
|
make_pair(tmpFlob->getFileId(), tmpFlob->getRecordId()),
|
|
curFlobOffset));
|
|
}
|
|
}
|
|
|
|
// change the flob header which is exported into the block
|
|
Flob newFlob = Flob::createFrom(
|
|
flobFileId, rcdId, curFlobOffset, mode, flobsz);
|
|
*tmpFlob = newFlob;
|
|
/*
|
|
The offset in the flob header is set differently, based on containsLob value.
|
|
If we export the whole tuple into one block, then the flob is always accessed
|
|
within the block, hence the offset is set as a relative position.
|
|
Or else, the flob is accessed separately in the flob file,
|
|
hence the offset should be set as a absolute position.
|
|
|
|
*/
|
|
if (writeCurrentFlob){
|
|
flobBlockOffset += flobsz;
|
|
lob += flobsz;
|
|
}
|
|
}
|
|
else if (flobsz < extensionLimit)
|
|
{
|
|
// put small flobs to the extension part
|
|
tmpFlob->read(ext, flobsz);
|
|
|
|
Flob newFlob = Flob::createFrom(
|
|
flobFileId, rcdId, extOffset, smode, flobsz);
|
|
*tmpFlob = newFlob;
|
|
|
|
extOffset += flobsz;
|
|
ext += flobsz;
|
|
}
|
|
}
|
|
|
|
currentSize = tupleType->GetAttributeType(i).size;
|
|
attributes[i]->Serialize(data, currentSize, offset);
|
|
offset += currentSize;
|
|
|
|
//restore old flob data
|
|
assert((size_t)attributes[i]->NumOfFLOBs() == attrFlobs.size());
|
|
for( int j = 0; j < attributes[i]->NumOfFLOBs(); j++) {
|
|
Flob *tmpFlob = attributes[i]->GetFLOB(j);
|
|
*tmpFlob = attrFlobs[j];
|
|
}
|
|
attrFlobs.clear();
|
|
}
|
|
else if (Attribute::Core == st)
|
|
{
|
|
assert( attributes[i]->NumOfFLOBs() == 0 );
|
|
|
|
currentSize = attributes[i]->SerializedSize();
|
|
attributes[i]->Serialize( data, currentSize, offset );
|
|
offset += currentSize;
|
|
}
|
|
else if (Attribute::Extension == st)
|
|
{
|
|
assert( attributes[i]->NumOfFLOBs() == 0 );
|
|
|
|
currentSize = sizeof(uint32_t);
|
|
|
|
WriteVar<uint32_t>( extOffset, data, offset );
|
|
SHOW(extOffset)
|
|
|
|
currentExtSize = attributes[i]->SerializedSize();
|
|
attributes[i]->Serialize(ext, currentExtSize, 0);
|
|
DEBUG_MSG( Array2HexStr(ext, currentExtSize) )
|
|
|
|
extOffset += currentExtSize;
|
|
ext += currentExtSize;
|
|
}
|
|
else
|
|
{
|
|
cerr << "ERROR: unknown storage type for attribute No " << i << endl;
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
TRACE_LEAVE
|
|
}
|
|
|
|
size_t Tuple::CalculateBlockSize( size_t& coreSize,
|
|
double& extSize,
|
|
double& size,
|
|
vector<double>& attrExtSize,
|
|
vector<double>& attrSize
|
|
) const
|
|
{
|
|
/*
|
|
These size values represent aggregate values for a single specific tuple.
|
|
|
|
----
|
|
coreSize = tupleType's size.
|
|
extensionSize = sum of each attribute's value size + (smallFlob sizes)
|
|
lobSize = sum of each attribute's bigFlob size
|
|
extSize += extensionSize
|
|
size += extensionSize + lobSize
|
|
return extensionSize
|
|
----
|
|
|
|
*/
|
|
|
|
TRACE_ENTER
|
|
int numOfAttr = tupleType->GetNoAttributes();
|
|
assert( attrExtSize.size() == attrSize.size() );
|
|
assert( (size_t)numOfAttr >= attrSize.size() );
|
|
|
|
coreSize = tupleType->GetCoreSize();
|
|
uint32_t extensionSize = 0;
|
|
double lobSize = 0.0;
|
|
|
|
// Calculate the size of the small FLOB data which will be
|
|
// saved together with the tuple attributes and save the LOBs
|
|
// in the lobFile.
|
|
for( int i = 0; i < numOfAttr; i++) {
|
|
Attribute::StorageType st = attributes[i]->GetStorageType();
|
|
|
|
uint16_t currentSize = 0;
|
|
size_t currentExtSize = 0;
|
|
|
|
// check storage type
|
|
if (st == Attribute::Default) {
|
|
currentSize = tupleType->GetAttributeType(i).coreSize;
|
|
}
|
|
else if (st == Attribute::Core) {
|
|
currentSize = attributes[i]->SerializedSize();
|
|
}
|
|
else if ( st == Attribute::Extension ) {
|
|
currentSize = sizeof(uint32_t);
|
|
currentExtSize = attributes[i]->SerializedSize();
|
|
}
|
|
else {
|
|
cerr << "ERROR: unknown storage type for attribute No "
|
|
<< i << endl;
|
|
assert(false);
|
|
}
|
|
|
|
extensionSize += currentExtSize;
|
|
attrExtSize[i] += (currentSize + currentExtSize);
|
|
attrSize[i] += (currentSize + currentExtSize);
|
|
|
|
SHOW(currentSize)
|
|
SHOW(currentExtSize)
|
|
SHOW(extensionSize)
|
|
|
|
// handle Flobs
|
|
double attrLobSize = 0.0;
|
|
for( int j = 0; j < attributes[i]->NumOfFLOBs(); j++) {
|
|
Flob* tmpFlob = attributes[i]->GetFLOB(j);
|
|
|
|
//assert( i >= 0 && (size_t)i < attrSize.size() );
|
|
const SmiSize tmpFlobSize = tmpFlob->getSize();
|
|
SHOW(tmpFlobSize)
|
|
if (tmpFlobSize < extensionLimit){ // small Flobs
|
|
extensionSize += tmpFlobSize;
|
|
attrExtSize[i] += tmpFlobSize;
|
|
} else { // big Flobs
|
|
attrLobSize += tmpFlobSize;
|
|
}
|
|
attrSize[i] += tmpFlobSize;
|
|
}
|
|
lobSize += attrLobSize;
|
|
} // for each attribute
|
|
|
|
extSize += extensionSize;
|
|
size += (extensionSize + lobSize);
|
|
|
|
TRACE_LEAVE
|
|
return extensionSize;
|
|
}
|
|
|
|
|
|
|
|
bool Tuple::Open( SmiRecordFile* tuplefile,
|
|
SmiFileId lobfileId,
|
|
SmiRecordId rid,
|
|
const bool dontReportError )
|
|
{
|
|
tupleId = rid;
|
|
this->tupleFile = tuplefile;
|
|
this->lobFileId = lobfileId;
|
|
SmiSize size;
|
|
char* data = tupleFile->GetData(rid, size, dontReportError);
|
|
if(!data){
|
|
return false;
|
|
}
|
|
// the first two bytes in data contain the rootsize
|
|
InitializeAttributes(tuplefile->GetFileId(),data);
|
|
free(data);
|
|
return true;
|
|
}
|
|
|
|
|
|
bool Tuple::ReadFrom(SmiFileId fileId, SmiRecord& record){
|
|
SmiSize size;
|
|
char* data = record.GetData( size);
|
|
if(!data){
|
|
return false;
|
|
}
|
|
InitializeAttributes(fileId,data);
|
|
free(data);
|
|
return true;
|
|
}
|
|
|
|
|
|
bool Tuple::Open( SmiRecordFile* tuplefile,
|
|
SmiFileId lobfileId,
|
|
SmiRecord* record,
|
|
const bool dontReportError ) {
|
|
SmiKey key;
|
|
key = record->GetKey();
|
|
key.GetKey( tupleId );
|
|
return Open(tuplefile, lobFileId, tupleId, dontReportError);
|
|
}
|
|
|
|
|
|
bool Tuple::Open(TupleFileIterator *iter)
|
|
{
|
|
if ( iter->MoreTuples() )
|
|
{
|
|
size_t rootSize;
|
|
|
|
char* data = iter->ReadNextTuple(rootSize);
|
|
|
|
if (data)
|
|
{
|
|
assert(rootSize < MAX_TUPLESIZE);
|
|
InitializeAttributes(iter->GetFileId(), data);
|
|
free(data);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#define FATAL_ERROR cerr << __FILE__ << "@" << __LINE__ << " - ERROR: "
|
|
|
|
|
|
char* Tuple::GetSMIBufferData(PrefetchingIterator* iter, uint16_t& rootSize)
|
|
{
|
|
// read size
|
|
const size_t rootSizeLen = sizeof(rootSize);
|
|
|
|
if( (size_t)iter->ReadCurrentData(&rootSize, rootSizeLen, 0) != rootSizeLen )
|
|
{
|
|
FATAL_ERROR << "iter->ReadCurrentData(...) failed!" << endl;
|
|
return 0;
|
|
}
|
|
|
|
//#undef SHOW
|
|
//#define SHOW(a) {cerr << " " << #a << " = " << a << endl;}
|
|
SHOW(rootSizeLen)
|
|
SHOW(rootSize)
|
|
//#define SHOW(a)
|
|
|
|
// read all attributes
|
|
assert(rootSize < MAX_TUPLESIZE);
|
|
uint16_t wholesize = sizeof(rootSize) + rootSize;
|
|
char* data = (char*) malloc ( wholesize );
|
|
char* ptr = data + sizeof(rootSize);
|
|
memcpy(data,&rootSize,sizeof(rootSize));
|
|
|
|
uint16_t k = (uint16_t)iter->ReadCurrentData( ptr,
|
|
rootSize,
|
|
rootSizeLen);
|
|
|
|
if( k != rootSize )
|
|
{
|
|
FATAL_ERROR << "iter->ReadCurrentData(...) failed!" << endl;
|
|
cerr << " k of " << rootSize << " bytes read." << endl;
|
|
return 0;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
|
|
void Tuple::InitializeAttributes(SmiFileId fileId,
|
|
char* src, bool containLOBs/* = false*/)
|
|
{
|
|
TRACE_ENTER
|
|
|
|
size_t offset = 0;
|
|
|
|
// indicate the area holding tuple's big flobs
|
|
size_t lobOffset = 0;
|
|
if(containLOBs)
|
|
{
|
|
u_int16_t tupleSize;
|
|
ReadVar<u_int16_t>(tupleSize, src, offset);
|
|
lobOffset = sizeof(u_int16_t) + tupleSize;
|
|
}
|
|
offset = sizeof(u_int16_t);
|
|
|
|
SHOW(offset)
|
|
|
|
for(size_t i=0;i<noAttributes;i++){
|
|
|
|
int algId = tupleType->GetAttributeType(i).algId;
|
|
int typeId = tupleType->GetAttributeType(i).typeId;
|
|
int sz = tupleType->GetAttributeType(i).size;
|
|
|
|
// create an instance of the specified type, which gives
|
|
// us an instance of a subclass of class Attribute.
|
|
Attribute* attr =
|
|
static_cast<Attribute*>( am->CreateObj(algId, typeId)(0).addr );
|
|
|
|
if ( attr->GetStorageType() == Attribute::Extension )
|
|
{
|
|
// extension serialization
|
|
uint32_t attrExtOffset = 0;
|
|
ReadVar<uint32_t>( attrExtOffset, src, offset );
|
|
SHOW(attrExtOffset)
|
|
attributes[i] = Attribute::Create( attr, &src[attrExtOffset],
|
|
0, algId, typeId);
|
|
|
|
SHOW(*attributes[i])
|
|
}
|
|
else
|
|
{
|
|
// serialization within root block
|
|
attributes[i] = Attribute::Create( attr, &src[offset], sz, algId, typeId);
|
|
|
|
int serialSize = attributes[i]->SerializedSize();
|
|
int readData = (serialSize > 0) ? serialSize : sz;
|
|
|
|
/*
|
|
//debug
|
|
if(offset+readData >wholesize + sizeof(uint16_t)){
|
|
cout << "try to read " << sz << " bytes at offset "
|
|
<< offset << " within a block of size " << wholesize
|
|
<< endl;
|
|
assert(false);
|
|
}
|
|
*/
|
|
|
|
offset += readData;
|
|
|
|
}
|
|
|
|
SHOW(offset)
|
|
SHOW(attributes[i]->IsDefined() )
|
|
|
|
|
|
|
|
// create uncontrollable Flobs
|
|
for(int k=0; k< attributes[i]->NumOfFLOBs();k++){
|
|
Flob* flob = attributes[i]->GetFLOB(k);
|
|
if(flob->getSize() < extensionLimit){
|
|
Flob::createFromBlock(*flob, src + flob->getOffset(),
|
|
flob->getSize(), true);
|
|
}
|
|
else if(containLOBs)
|
|
{
|
|
assert(lobOffset > 0);
|
|
Flob::createFromBlock(*flob, src + lobOffset,
|
|
flob->getSize(), true);
|
|
flob->resize(flob->getSize()); //swap flob into a limited memory cache
|
|
//resize function will make the ftext attribute be empty
|
|
lobOffset += flob->getSize();
|
|
}
|
|
}
|
|
// Call the Initialize function for every attribute
|
|
// and initialize the reference counter
|
|
attributes[i]->Initialize(fileId, tupleId, i );
|
|
attributes[i]->InitRefs();
|
|
} // end for
|
|
|
|
TRACE_LEAVE
|
|
|
|
}
|
|
|
|
void Tuple::InitializeSomeAttributes( SmiFileId fileId,
|
|
const list<int>& aIds,
|
|
char* src )
|
|
{
|
|
TRACE_ENTER
|
|
|
|
list<int>::const_iterator iter = aIds.begin();
|
|
for( ; iter != aIds.end(); iter++ )
|
|
{
|
|
int i = *iter;
|
|
DEBUG_MSG( "Attribute #" << i << endl )
|
|
|
|
size_t offset = tupleType->GetAttributeType(i).offset;
|
|
int algId = tupleType->GetAttributeType(i).algId;
|
|
int typeId = tupleType->GetAttributeType(i).typeId;
|
|
int sz = tupleType->GetAttributeType(i).size;
|
|
|
|
SHOW(offset)
|
|
SHOW(sz)
|
|
|
|
// create an instance of the specified type, which gives
|
|
// us an instance of a subclass of class Attribute.
|
|
Attribute* attr =
|
|
static_cast<Attribute*>( am->CreateObj(algId, typeId)(0).addr );
|
|
|
|
if ( attr->GetStorageType() == Attribute::Extension )
|
|
{
|
|
uint32_t attrExtOffset = 0;
|
|
ReadVar<uint32_t>( attrExtOffset, src, offset );
|
|
|
|
SHOW(attrExtOffset)
|
|
|
|
attributes[i] = Attribute::Create( attr, &src[attrExtOffset],
|
|
0, algId, typeId);
|
|
}
|
|
else
|
|
{
|
|
attributes[i] = Attribute::Create( attr, &src[offset], sz, algId, typeId);
|
|
|
|
int serialSize = attributes[i]->SerializedSize();
|
|
int readData = (serialSize > 0) ? serialSize : sz;
|
|
|
|
SHOW(serialSize)
|
|
SHOW(readData)
|
|
DEBUG_MSG( Array2HexStr(src, readData, offset) )
|
|
|
|
offset += readData;
|
|
|
|
// Note: No special treatment for Flobs needed!
|
|
}
|
|
|
|
SHOW( attributes[i]->IsDefined() )
|
|
// create uncontrollable Flobs
|
|
for(int k=0; k< attributes[i]->NumOfFLOBs();k++){
|
|
Flob* flob = attributes[i]->GetFLOB(k);
|
|
if(flob->getSize() < extensionLimit){
|
|
Flob::createFromBlock(*flob, src + flob->getOffset(),
|
|
flob->getSize(), true);
|
|
}
|
|
}
|
|
|
|
// Call the Initialize function for every attribute
|
|
// and initialize the reference counter
|
|
attributes[i]->Initialize(fileId, tupleId, i);
|
|
attributes[i]->InitRefs();
|
|
|
|
}
|
|
|
|
TRACE_LEAVE
|
|
}
|
|
|
|
/*
|
|
Here the block contains only the data for the tuple,
|
|
without Flob and the length header that is a int16.
|
|
Therefore, the flobOffset should be reduced accordingly.
|
|
|
|
*/
|
|
void Tuple::InitializeNoFlobAttributes(SmiFileId fileId,
|
|
char* src,
|
|
string flobFile/* = ""*/, size_t flobOffset/* = 0*/)
|
|
{
|
|
TRACE_ENTER
|
|
|
|
size_t offset = 0;
|
|
|
|
for (size_t i = 0; i < noAttributes; i++)
|
|
{
|
|
int algId = tupleType->GetAttributeType(i).algId;
|
|
int typeId = tupleType->GetAttributeType(i).typeId;
|
|
int sz = tupleType->GetAttributeType(i).size;
|
|
|
|
// create an instance of the specified type, which gives
|
|
// us an instance of a subclass of class Attribute.
|
|
Attribute* attr =
|
|
static_cast<Attribute*>( am->CreateObj(algId, typeId)(0).addr );
|
|
|
|
if ( attr->GetStorageType() == Attribute::Extension )
|
|
{
|
|
// extension serialization
|
|
uint32_t attrExtOffset = 0;
|
|
ReadVar<uint32_t>( attrExtOffset, src, offset );
|
|
SHOW(attrExtOffset)
|
|
attrExtOffset -= sizeof(u_int16_t);
|
|
attributes[i] = Attribute::Create( attr, &src[attrExtOffset],
|
|
0, algId, typeId);
|
|
}
|
|
else
|
|
{
|
|
// serialization within root block
|
|
attributes[i] = Attribute::Create( attr, &src[offset], sz, algId, typeId);
|
|
|
|
int serialSize = attributes[i]->SerializedSize();
|
|
int readData = (serialSize > 0) ? serialSize : sz;
|
|
offset += readData;
|
|
}
|
|
|
|
//process FLOBS, set their FlobID, instead of reading them out
|
|
for (int k = 0; k < attributes[i]->NumOfFLOBs(); k++){
|
|
Flob* flob = attributes[i]->GetFLOB(k);
|
|
if(flob->getSize() < extensionLimit){
|
|
Flob::createFromBlock(*flob,
|
|
src + flob->getOffset() - sizeof(u_int16_t),
|
|
flob->getSize(), true);
|
|
}
|
|
else{
|
|
if (!flobFile.empty()){
|
|
Flob::setExFile(*flob, flobFile, flob->getSize(), flobOffset);
|
|
}
|
|
}
|
|
}
|
|
// Call the Initialize function for every attribute
|
|
// and initialize the reference counter
|
|
attributes[i]->Initialize(fileId, tupleId, i);
|
|
attributes[i]->InitRefs();
|
|
} // end for
|
|
|
|
TRACE_LEAVE
|
|
}
|
|
|
|
void Tuple::readLocalFlobFile(const string flobFilePath)
|
|
{
|
|
TRACE_ENTER
|
|
|
|
for (size_t i = 0; i < noAttributes; i++)
|
|
{
|
|
for (int k = 0; k < attributes[i]->NumOfFLOBs(); k++)
|
|
{
|
|
Flob* flob = attributes[i]->GetFLOB(k);
|
|
SmiSize bsize = flob->getSize();
|
|
if (bsize >= extensionLimit){
|
|
std::stringstream fileId;
|
|
fileId << flob->getFileId();
|
|
string flobFile = flobFilePath + fileId.str();
|
|
|
|
Flob::readExFile(*flob, flobFile, bsize, flob->getOffset());
|
|
}
|
|
}
|
|
}
|
|
|
|
TRACE_LEAVE
|
|
}
|
|
|
|
|
|
bool Tuple::Open( SmiRecordFile *tuplefile,
|
|
SmiFileId lobfileId,
|
|
PrefetchingIterator *iter )
|
|
{
|
|
TRACE_ENTER
|
|
DEBUG_MSG("Open::Prefetch")
|
|
|
|
iter->ReadCurrentRecordNumber( tupleId );
|
|
this->tupleFile = tuplefile;
|
|
this->lobFileId = lobfileId;
|
|
|
|
uint16_t rootSize = 0;
|
|
char* data = GetSMIBufferData(iter, rootSize);
|
|
|
|
if (data) {
|
|
InitializeAttributes(tuplefile->GetFileId(),data);
|
|
free ( data );
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
|
|
TRACE_LEAVE
|
|
}
|
|
|
|
|
|
|
|
bool Tuple::OpenPartial( TupleType* newtype, const list<int>& attrIdList,
|
|
SmiRecordFile *tuplefile,
|
|
SmiFileId lobfileId,
|
|
PrefetchingIterator *iter )
|
|
{
|
|
TRACE_ENTER
|
|
DEBUG_MSG("OpenPartial using PrefetchingIterator")
|
|
|
|
iter->ReadCurrentRecordNumber( tupleId );
|
|
this->tupleFile = tuplefile;
|
|
this->lobFileId = lobfileId;
|
|
|
|
uint16_t rootSize = 0;
|
|
char* data = GetSMIBufferData(iter, rootSize);
|
|
|
|
if (data) {
|
|
InitializeSomeAttributes(tuplefile->GetFileId(),
|
|
attrIdList, data);
|
|
ChangeTupleType(newtype, attrIdList);
|
|
free ( data );
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
|
|
TRACE_LEAVE
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
3.3 Implementation of the class ~Tuple~
|
|
|
|
This class implements the persistent representation of the type
|
|
constructor ~tuple~.
|
|
|
|
*/
|
|
Tuple *Tuple::RestoreFromList( ListExpr typeInfo, ListExpr value,
|
|
int errorPos, ListExpr& errorInfo,
|
|
bool& correct )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ListExpr Tuple::SaveToList( ListExpr typeInfo )
|
|
{
|
|
return nl->TheEmptyList();
|
|
}
|
|
|
|
const TupleId& Tuple::GetTupleId() const
|
|
{
|
|
return (TupleId&)tupleId;
|
|
}
|
|
|
|
void Tuple::SetTupleId( const TupleId& id )
|
|
{
|
|
tupleId = (SmiRecordId)id;
|
|
}
|
|
|
|
void Tuple::PutAttribute( const int index, Attribute* attr )
|
|
{
|
|
if( attributes[index] != 0 )
|
|
attributes[index]->DeleteIfAllowed();
|
|
attributes[index] = attr;
|
|
|
|
recomputeExtSize = true;
|
|
recomputeSize = true;
|
|
}
|
|
|
|
/*
|
|
The next function updates the tuple by replacing the old attributes at the positions given
|
|
by 'changedIndices' with the new ones from 'newAttrs'. If the old attribute
|
|
had FLOBs they are destroyed so that there is no garbage left on the disk.
|
|
|
|
*/
|
|
void Tuple::UpdateAttributes( const vector<int>& changedIndices,
|
|
const vector<Attribute*>& newAttrs,
|
|
double& extSize, double& size,
|
|
vector<double>& attrExtSize,
|
|
vector<double>& attrSize )
|
|
{
|
|
int index;
|
|
for ( size_t i = 0; i < changedIndices.size(); i++)
|
|
{
|
|
index = changedIndices[i];
|
|
assert( index >= 0 && index < GetNoAttributes() );
|
|
assert( attributes[index] != 0 );
|
|
for (int j = 0;
|
|
j < attributes[index]->NumOfFLOBs();
|
|
j++)
|
|
{
|
|
Flob *tmpFlob = attributes[index]->GetFLOB(j);
|
|
|
|
assert( index >= 0 && (size_t)index < attrSize.size() );
|
|
SmiSize fsz = tmpFlob->getSize();
|
|
|
|
attrSize[index] -= fsz;
|
|
size -= fsz;
|
|
|
|
if( fsz < extensionLimit )
|
|
{
|
|
assert( index >= 0 && (size_t)index < attrExtSize.size() );
|
|
attrExtSize[index] -= fsz;
|
|
extSize -= fsz;
|
|
}
|
|
tmpFlob->destroy();
|
|
}
|
|
|
|
attributes[index]->DeleteIfAllowed();
|
|
attributes[index] = newAttrs[i];
|
|
}
|
|
UpdateSave( changedIndices,
|
|
extSize, size,
|
|
attrExtSize, attrSize );
|
|
|
|
recomputeExtSize = true;
|
|
recomputeSize = true;
|
|
}
|
|
|
|
/*
|
|
For transporting complete tuples between different Secondo system
|
|
without change, transfer the tuple value into a transportable
|
|
string follow Base 64. Then the cost of the ~Out~ function
|
|
can be avoided.
|
|
|
|
In the contrast, construct a tuple object by reading this string,
|
|
can avoid the ~In~ function cost.
|
|
|
|
*/
|
|
string Tuple::WriteToBinStr()
|
|
{
|
|
size_t coreSize = 0;
|
|
size_t extensionSize = 0;
|
|
size_t flobSize = 0;
|
|
size_t blockSize = GetBlockSize(coreSize, extensionSize, flobSize);
|
|
|
|
//The tuple block need two values to indicate the whole block size
|
|
//and only the tuple's core and extension length.
|
|
//blockSize += sizeof(blockSize) + sizeof(u_int16_t);
|
|
char data[blockSize];
|
|
WriteToBin(data, coreSize, extensionSize, flobSize);
|
|
|
|
Base64 b64;
|
|
string binStr;
|
|
b64.encode(data, blockSize, binStr);
|
|
|
|
return stringutils::replaceAll(binStr, "\n", "");
|
|
}
|
|
|
|
void Tuple::ReadFromBinStr(SmiFileId fileId,string& binStr)
|
|
{
|
|
Base64 b64;
|
|
int sizeDecoded = b64.sizeDecoded(binStr.size());
|
|
char bytes[sizeDecoded];
|
|
int result = b64.decode( binStr, bytes );
|
|
assert( result <= sizeDecoded );
|
|
|
|
ReadFromBin(fileId, bytes);
|
|
}
|
|
|
|
/*
|
|
Put complete tuple's binary data into an allocated memory buffer,
|
|
including its big Flobs.
|
|
The head value of the block named ~blockSize~ is used to
|
|
indicate the whole block's size
|
|
|
|
----
|
|
blockSize = sizeof(blockSize) + tupleSize + flobSize
|
|
tupleSize = sizeof(tupleSize) + coreSize + extensionSize
|
|
----
|
|
|
|
*/
|
|
void Tuple::WriteToBin(char* buf,
|
|
size_t coreSize, size_t extensionSize, size_t flobSize)
|
|
{
|
|
u_int32_t blockSize =
|
|
sizeof(u_int32_t) + sizeof(u_int16_t)
|
|
+ coreSize + extensionSize + flobSize;
|
|
memcpy(buf, &blockSize, sizeof(blockSize));
|
|
|
|
WriteToBlock(buf + sizeof(u_int32_t),
|
|
coreSize, extensionSize,
|
|
false, 0, 0, true);
|
|
}
|
|
|
|
/*
|
|
Put the tuple except its big flob data into an allocated memory buffer.
|
|
However, it still follows the format of the block that contains the flob,
|
|
therefore, the head value ~blockSize~ here is:
|
|
|
|
----
|
|
blockSize = sizeof(blockSize) + tupleSize
|
|
tupleSize = sizeof(tupleSize) + coreSize + extensionSize
|
|
----
|
|
|
|
*/
|
|
void Tuple::WriteTupleToBin(char* buf,
|
|
size_t coreSize, size_t extensionSize)
|
|
{
|
|
u_int32_t blockSize =
|
|
sizeof(u_int32_t) + sizeof(u_int16_t)
|
|
+ coreSize + extensionSize;
|
|
memcpy(buf, &blockSize, sizeof(blockSize));
|
|
|
|
WriteToBlock(buf + sizeof(u_int32_t),
|
|
coreSize, extensionSize,
|
|
true, 0, 0, false);
|
|
}
|
|
|
|
size_t Tuple::GetBlockSize( size_t& coreSize,
|
|
size_t& extensionSize,
|
|
size_t& flobSize,
|
|
vector<double>* aes/* = 0*/,
|
|
vector<double>* as/* = 0*/) const
|
|
{
|
|
// Get the memory block size of a single tuple, includes its big Flobs,
|
|
// and head size values.
|
|
|
|
double extSize = 0.0;
|
|
double size = 0.0;
|
|
vector<double> *attrExtSize = aes;
|
|
vector<double> *attrSize = as;
|
|
if (!attrExtSize)
|
|
attrExtSize = new vector<double>(tupleType->GetNoAttributes());
|
|
if (!attrSize)
|
|
attrSize = new vector<double>(tupleType->GetNoAttributes());
|
|
|
|
// vector<double> attrExtSize(tupleType->GetNoAttributes());
|
|
// vector<double> attrSize(tupleType->GetNoAttributes());
|
|
|
|
extSize = tupleType->GetCoreSize();
|
|
size = tupleType->GetCoreSize();
|
|
extensionSize = CalculateBlockSize( coreSize, extSize,
|
|
size, *attrExtSize,
|
|
*attrSize);
|
|
|
|
if (!aes) delete attrExtSize;
|
|
if (!as) delete attrSize;
|
|
|
|
flobSize = (size_t)size - extensionSize - tupleType->GetCoreSize();
|
|
//size = tupleType's core size + extensionSize + flobSize (big flobs)
|
|
return (size_t)size + sizeof(u_int32_t) + sizeof(u_int16_t);
|
|
}
|
|
|
|
/*
|
|
Read a tuple from a binary block, created by ~WriteToBin~ function.
|
|
The block contains a tuple's complete data, including its big Flobs.
|
|
|
|
*/
|
|
u_int32_t Tuple::ReadFromBin(SmiFileId fileId,
|
|
char* buf, u_int32_t bSize/* = 0*/)
|
|
{
|
|
assert(buf);
|
|
|
|
u_int32_t blockSize;
|
|
if (bSize == 0)
|
|
{
|
|
memcpy(&blockSize, buf, sizeof(blockSize));
|
|
buf += sizeof(blockSize);
|
|
|
|
InitializeAttributes(fileId, buf, true);
|
|
return blockSize;
|
|
}
|
|
else
|
|
{
|
|
blockSize = bSize;
|
|
u_int16_t tupleSize;
|
|
size_t offset = 0;
|
|
ReadVar<u_int16_t>(tupleSize, buf, offset);
|
|
bool containLOBs =
|
|
( (u_int32_t)(tupleSize + sizeof(tupleSize)) != blockSize);
|
|
|
|
InitializeAttributes(fileId,buf, containLOBs);
|
|
return blockSize;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Prepare this function for ~ffeed2~ operator
|
|
|
|
Read the tuple from a binary block created by ~WriteToBin~.
|
|
Although the file data contains the whole tuple data including the Flob,
|
|
this function reads only its root data and leaves the Flob untouched.
|
|
Consequently, the created tuple sets the Flob mode to 2 and
|
|
links the Flob reference to the flobFile.
|
|
|
|
*/
|
|
u_int32_t Tuple::ReadTupleFromBin(SmiFileId fileId,char* buf, u_int32_t bSize,
|
|
string flobFile, size_t flobOffset)
|
|
{
|
|
assert(buf);
|
|
InitializeNoFlobAttributes(fileId, buf, flobFile, flobOffset);
|
|
return bSize;
|
|
}
|
|
|
|
void Tuple::ReadTupleFromBin(SmiFileId fileId,char* buf){
|
|
assert(buf);
|
|
InitializeNoFlobAttributes(fileId,buf);
|
|
}
|
|
|
|
|
|
|
|
size_t Tuple::HashValue() const {
|
|
size_t hash = 0;
|
|
for(int i=0;i<GetNoAttributes();i++) {
|
|
hash += GetAttribute(i)->HashValue();
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
std::ostream& Tuple::PrintWithNames(ostream& out,
|
|
std::vector<std::string>& names){
|
|
if(GetNoAttributes() != (int) names.size()){
|
|
out << "invalid call of Tupple::PrintWithNames" << endl;
|
|
return out;
|
|
}
|
|
for(size_t i=0;i<names.size();i++){
|
|
out << names[i] << " : ";
|
|
GetAttribute(i)->PrintFormatted(out);
|
|
out << endl;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
|
|
|
|
|
|
TupleFileIterator::TupleFileIterator(TupleFile& f)
|
|
: tupleFile(f)
|
|
, data(0)
|
|
, size(0)
|
|
{
|
|
// First close stream if it still open
|
|
tupleFile.Close();
|
|
|
|
// Open stream for reading
|
|
tupleFile.stream = fopen((char*)tupleFile.pathName.c_str(), "rb");
|
|
|
|
if( !tupleFile.stream )
|
|
{
|
|
cerr << "TupleFileIterator: Cannot open file '"
|
|
<< tupleFile.pathName << "' for binary reading!\n" << endl;
|
|
return;
|
|
}
|
|
|
|
// reposition stream to beginning of file
|
|
rewind(tupleFile.stream);
|
|
|
|
if ( tupleFile.traceMode )
|
|
{
|
|
cmsg.info() << "TupleFile " << tupleFile.pathName
|
|
<< " opened for reading." << endl;
|
|
cmsg.send();
|
|
}
|
|
|
|
// Read data of first tuple
|
|
data = readData(size);
|
|
}
|
|
|
|
TupleFileIterator::~TupleFileIterator()
|
|
{
|
|
tupleFile.Close();
|
|
}
|
|
|
|
Tuple* TupleFileIterator::GetNextTuple()
|
|
{
|
|
if ( data )
|
|
{
|
|
Tuple* t = new Tuple((TupleType*)tupleFile.tupleType);
|
|
|
|
// Read tuple data from disk buffer
|
|
if ( t->Open(this) )
|
|
{
|
|
return t;
|
|
}
|
|
else
|
|
{
|
|
delete t;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
char* TupleFileIterator::ReadNextTuple(size_t& size)
|
|
{
|
|
if ( data )
|
|
{
|
|
char* tmp = data;
|
|
|
|
size = this->size;
|
|
|
|
// Read data of next tuple
|
|
data = readData(this->size);
|
|
|
|
return tmp;
|
|
}
|
|
else
|
|
{
|
|
size = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
char* TupleFileIterator::readData(size_t& size)
|
|
{
|
|
if ( tupleFile.stream )
|
|
{
|
|
uint16_t blockSize;
|
|
|
|
// Read the size of the next data block
|
|
size_t rc = fread(&blockSize, 1, sizeof(blockSize), tupleFile.stream);
|
|
|
|
if ( feof(tupleFile.stream) )
|
|
{
|
|
size = 0;
|
|
return 0;
|
|
}
|
|
else if ( rc < sizeof(blockSize) )
|
|
{
|
|
cerr << "TupleFileIterator::ReadNextTuple: error "
|
|
<< "reading data block size in file '"
|
|
<< tupleFile.pathName << "'!\n" << endl;
|
|
size = 0;
|
|
return 0;
|
|
}
|
|
|
|
size = sizeof(blockSize) + blockSize;
|
|
|
|
// Allocate a single memory block for data block size and tuple data
|
|
char* data = (char*)malloc(size);
|
|
|
|
// Store block size in memory block
|
|
memcpy(data,&blockSize,sizeof(blockSize));
|
|
|
|
// Read tuple data into memory block
|
|
rc = fread(data + sizeof(blockSize), 1, blockSize, tupleFile.stream);
|
|
|
|
|
|
if ( rc < blockSize )
|
|
{
|
|
cerr << "TupleFileIterator::ReadNextTuple: error "
|
|
<< "reading tuple data block in file '"
|
|
<< tupleFile.pathName << "'!\n" << endl;
|
|
size = 0;
|
|
return 0;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
else
|
|
{
|
|
size = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
SmiFileId TupleFileIterator::GetFileId() const{
|
|
return tupleFile.GetFileId();
|
|
}
|
|
|
|
bool TupleFile::traceMode = false;
|
|
|
|
TupleFile::TupleFile( TupleType* tupleType,
|
|
const size_t bufferSize )
|
|
: tupleType(tupleType)
|
|
, bufferSize(bufferSize)
|
|
, stream(0)
|
|
, tupleCount(0)
|
|
, totalSize(0)
|
|
, totalExtSize(0)
|
|
{
|
|
this->tupleType->IncReference();
|
|
|
|
// create a unique temporary filename
|
|
string str = FileSystem::GetCurrentFolder();
|
|
FileSystem::AppendItem(str, "tmp");
|
|
FileSystem::AppendItem(str, "TF");
|
|
pathName = FileSystem::MakeTemp(str);
|
|
|
|
if ( traceMode )
|
|
{
|
|
cmsg.info() << "TupleFile " << pathName << " created." << endl;
|
|
cmsg.send();
|
|
}
|
|
}
|
|
|
|
TupleFile::TupleFile( TupleType* tupleType,
|
|
string pathName,
|
|
const size_t bufferSize )
|
|
: tupleType(tupleType)
|
|
, pathName(pathName)
|
|
, bufferSize(bufferSize)
|
|
, stream(0)
|
|
, tupleCount(0)
|
|
, totalSize(0)
|
|
, totalExtSize(0)
|
|
{
|
|
this->tupleType->IncReference();
|
|
|
|
if ( pathName == "" )
|
|
{
|
|
// create a unique temporary filename
|
|
string str = FileSystem::GetCurrentFolder();
|
|
FileSystem::AppendItem(str, "tmp");
|
|
FileSystem::AppendItem(str, "TF");
|
|
pathName = FileSystem::MakeTemp(str);
|
|
}
|
|
|
|
if ( traceMode )
|
|
{
|
|
cmsg.info() << "TupleFile " << pathName << " created." << endl;
|
|
cmsg.send();
|
|
}
|
|
}
|
|
|
|
TupleFile::~TupleFile()
|
|
{
|
|
// close stream if still open
|
|
Close();
|
|
|
|
// delete reference to tuple type
|
|
tupleType->DeleteIfAllowed();
|
|
|
|
// delete temporary disk file
|
|
FileSystem::DeleteFileOrFolder(pathName);
|
|
|
|
if ( traceMode )
|
|
{
|
|
cmsg.info() << "TupleFile " << pathName << " deleted." << endl;
|
|
cmsg.send();
|
|
}
|
|
}
|
|
|
|
bool TupleFile::Open()
|
|
{
|
|
if ( tupleCount == 0 )
|
|
{
|
|
// Open fresh binary stream
|
|
stream = fopen(pathName.c_str(), "w+b" );
|
|
|
|
if ( traceMode )
|
|
{
|
|
cmsg.info() << "TupleFile opened (w+b)." << endl;
|
|
cmsg.send();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Append to existing file if tupleCount > 0
|
|
stream = fopen(pathName.c_str(), "a+b" );
|
|
|
|
if ( traceMode )
|
|
{
|
|
cmsg.info() << "TupleFile opened (a+b)" << endl;
|
|
cmsg.send();
|
|
}
|
|
}
|
|
|
|
if( !stream )
|
|
{
|
|
cerr << "TupleFile::Open(): Cannot open file '"
|
|
<< pathName << "'!\n" << endl;
|
|
cerr << "TupleCount: " << tupleCount << endl;
|
|
return false;
|
|
}
|
|
|
|
// Set stream buffer size
|
|
if ( setvbuf(stream, 0, bufferSize >= 2 ? _IOFBF : _IONBF, bufferSize) != 0 )
|
|
{
|
|
cerr << "TupleFile::Open(): illegal buffer type or size specified '"
|
|
<< bufferSize << "'!\n" << endl;
|
|
return false;
|
|
}
|
|
|
|
if ( traceMode )
|
|
{
|
|
cmsg.info() << "TupleFile " << pathName << " opened for writing." << endl;
|
|
cmsg.send();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void TupleFile::Close()
|
|
{
|
|
if ( stream != 0 )
|
|
{
|
|
if ( fclose(stream) == EOF )
|
|
{
|
|
cerr << "TupleFile::Close(): error while closing file '"
|
|
<< pathName << "'!\n" << endl;
|
|
}
|
|
stream = 0;
|
|
|
|
if ( traceMode )
|
|
{
|
|
cmsg.info() << "TupleFile " << pathName << " closed." << endl;
|
|
cmsg.send();
|
|
}
|
|
}
|
|
}
|
|
|
|
void TupleFile::Append(Tuple* t)
|
|
{
|
|
t->Save(*this);
|
|
}
|
|
|
|
void TupleFile::Append(char *data, size_t core, size_t ext)
|
|
{
|
|
if ( stream == 0 )
|
|
{
|
|
this->Open();
|
|
}
|
|
|
|
uint16_t size = core + ext;
|
|
|
|
|
|
size_t rc = fwrite(data, 1, sizeof(size) + size, stream);
|
|
|
|
// check the number of written
|
|
if ( rc < sizeof(size) + size )
|
|
{
|
|
cerr << "TupleFile::Append(): error writing to file '"
|
|
<< pathName << "'!\n" << endl;
|
|
cerr << "(" << sizeof(size) + size
|
|
<< ") bytes should be written, but only ("
|
|
<< rc << ") bytes were written" "!\n" << endl;
|
|
return;
|
|
}
|
|
|
|
fflush(stream);
|
|
|
|
tupleCount++;
|
|
totalSize += size;
|
|
totalExtSize += ext;
|
|
|
|
return;
|
|
}
|
|
|
|
TupleFileIterator* TupleFile::MakeScan()
|
|
{
|
|
return new TupleFileIterator(*this);
|
|
}
|
|
|
|
/*
|
|
3.9 Class ~TupleBuffer~
|
|
|
|
This class is used to collect tuples for sorting, for example, or
|
|
to do a cartesian product. In this persistent version, if the buffer
|
|
is small it will be stored in memory and if it exceeds the allowed size,
|
|
the current buffer contents will be flushed to disk.
|
|
|
|
The Iterator will first retrieve the tuples on disk and afterwards the remaining
|
|
tuples which reside in memory.
|
|
|
|
*/
|
|
|
|
TupleBuffer::TupleBuffer( const size_t maxMemorySize ):
|
|
MAX_MEMORY_SIZE( maxMemorySize ),
|
|
diskBuffer( 0 ),
|
|
inMemory( true ),
|
|
traceFlag( RTFlag::isActive("RA:TupleBufferInfo") ),
|
|
totalMemSize( 0 ),
|
|
totalExtSize( 0),
|
|
totalSize( 0 )
|
|
{
|
|
if (traceFlag)
|
|
{
|
|
cmsg.info() << "New Instance of TupleBuffer with size "
|
|
<< maxMemorySize / 1024 << "kb "
|
|
<< " address = " << (void*)this << endl;
|
|
cmsg.send();
|
|
}
|
|
}
|
|
|
|
/*
|
|
The constructor.
|
|
|
|
*/
|
|
|
|
TupleBuffer::~TupleBuffer()
|
|
{
|
|
updateDataStatistics();
|
|
clearAll();
|
|
if( !inMemory ) {
|
|
diskBuffer->DeleteAndTruncate();
|
|
diskBuffer = 0;
|
|
}
|
|
}
|
|
|
|
void TupleBuffer::clearAll()
|
|
{
|
|
for( TupleVec::iterator it = memoryBuffer.begin();
|
|
it != memoryBuffer.end(); it++ )
|
|
{
|
|
//cout << (void*) *it << " - " << (*it)->GetNumOfRefs() << endl;
|
|
//if (*it != 0) {
|
|
(*it)->DeleteIfAllowed();
|
|
//}
|
|
}
|
|
memoryBuffer.clear();
|
|
totalSize=0;
|
|
}
|
|
|
|
/*
|
|
The destructor.
|
|
|
|
*/
|
|
|
|
int TupleBuffer::GetNoTuples() const
|
|
{
|
|
if( inMemory )
|
|
return memoryBuffer.size();
|
|
else
|
|
return diskBuffer->GetNoTuples();
|
|
}
|
|
|
|
size_t TupleBuffer::FreeBytes() const
|
|
{
|
|
if (MAX_MEMORY_SIZE > totalMemSize) {
|
|
return MAX_MEMORY_SIZE - static_cast<size_t>( ceil(totalMemSize) );
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
double TupleBuffer::GetTotalRootSize() const
|
|
{
|
|
if( IsEmpty() )
|
|
return 0;
|
|
|
|
if (inMemory)
|
|
return GetNoTuples() * memoryBuffer[0]->GetRootSize();
|
|
else
|
|
return diskBuffer->GetTupleType()->GetTotalSize();
|
|
}
|
|
|
|
|
|
double TupleBuffer::GetTotalRootSize(int i) const
|
|
{
|
|
if( IsEmpty() )
|
|
return 0;
|
|
|
|
if (inMemory)
|
|
return memoryBuffer[0]->GetRootSize(i);
|
|
else
|
|
return diskBuffer->GetTupleType()->GetTotalSize();
|
|
}
|
|
|
|
double TupleBuffer::GetTotalExtSize() const
|
|
{
|
|
if( inMemory )
|
|
return totalExtSize;
|
|
else
|
|
return diskBuffer->GetTotalExtSize();
|
|
}
|
|
|
|
double TupleBuffer::GetTotalExtSize(int i) const
|
|
{
|
|
if( IsEmpty() )
|
|
return 0;
|
|
|
|
if( inMemory )
|
|
return memoryBuffer[0]->GetExtSize(i);
|
|
else
|
|
return diskBuffer->GetTotalExtSize(i);
|
|
}
|
|
|
|
|
|
double TupleBuffer::GetTotalSize() const
|
|
{
|
|
if( inMemory )
|
|
return totalSize;
|
|
else
|
|
return diskBuffer->GetTotalSize();
|
|
}
|
|
|
|
double TupleBuffer::GetTotalSize(int i) const
|
|
{
|
|
if( IsEmpty() )
|
|
return 0;
|
|
|
|
if( inMemory )
|
|
return memoryBuffer[0]->GetSize(i);
|
|
else
|
|
return diskBuffer->GetTotalSize(i);
|
|
}
|
|
|
|
void
|
|
TupleBuffer::updateDataStatistics() {
|
|
|
|
static long& writtenData_Bytes = Counter::getRef(Symbol::CTR_TBUF_BYTES_W());
|
|
static long& writtenData_Pages = Counter::getRef(Symbol::CTR_TBUF_PAGES_W());
|
|
|
|
if (diskBuffer)
|
|
writtenData_Bytes += (long)ceil( diskBuffer->GetTotalExtSize() );
|
|
|
|
writtenData_Pages = writtenData_Bytes / WinUnix::getPageSize();
|
|
}
|
|
|
|
bool TupleBuffer::IsEmpty() const
|
|
{
|
|
if( inMemory )
|
|
return memoryBuffer.empty();
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void TupleBuffer::Clear()
|
|
{
|
|
updateDataStatistics();
|
|
if( inMemory )
|
|
{
|
|
clearAll();
|
|
}
|
|
else
|
|
{
|
|
diskBuffer->DeleteAndTruncate();
|
|
diskBuffer = 0;
|
|
inMemory = true;
|
|
}
|
|
}
|
|
|
|
void TupleBuffer::AppendTuple( Tuple *t )
|
|
{
|
|
|
|
static long& appendCalls = Counter::getRef("RA:TupleBuf:diskBufferAppend");
|
|
if( inMemory )
|
|
{
|
|
if( totalMemSize + t->GetMemSize() <=
|
|
MAX_MEMORY_SIZE )
|
|
{
|
|
t->IncReference();
|
|
memoryBuffer.push_back( t );
|
|
totalMemSize += t->GetMemSize();
|
|
totalSize += t->GetSize();
|
|
totalExtSize += t->GetExtSize();
|
|
}
|
|
else
|
|
{
|
|
if (traceFlag)
|
|
{
|
|
cmsg.info() << "Changing TupleBuffer's state from inMemory "
|
|
<< "-> !inMemory" << endl;
|
|
cmsg.send();
|
|
}
|
|
diskBuffer =
|
|
new Relation( t->GetTupleType(), true );
|
|
|
|
vector<Tuple*>::iterator iter =
|
|
memoryBuffer.begin();
|
|
while( iter != memoryBuffer.end() )
|
|
{
|
|
Tuple* tuple = *iter;
|
|
tuple->PinAttributes();
|
|
diskBuffer->AppendTupleNoLOBs( tuple );
|
|
appendCalls++;
|
|
tuple->DeleteIfAllowed();
|
|
iter++;
|
|
}
|
|
memoryBuffer.clear();
|
|
totalMemSize = 0;
|
|
totalExtSize = 0;
|
|
totalSize = 0;
|
|
t->PinAttributes();
|
|
diskBuffer->AppendTupleNoLOBs( t );
|
|
appendCalls++;
|
|
inMemory = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
t->PinAttributes();
|
|
return diskBuffer->AppendTupleNoLOBs( t );
|
|
}
|
|
}
|
|
|
|
Tuple *TupleBuffer::GetTuple( const TupleId& id,
|
|
const bool dontReportError ) const
|
|
{
|
|
if( inMemory )
|
|
{
|
|
Tuple* res = GetTupleAtPos( id );
|
|
res->IncReference();
|
|
return res;
|
|
}
|
|
else
|
|
{
|
|
return diskBuffer->GetTuple( id+1, dontReportError );
|
|
}
|
|
}
|
|
|
|
Tuple* TupleBuffer::GetTupleAtPos( const size_t pos ) const
|
|
{
|
|
if( inMemory )
|
|
{
|
|
if( pos < memoryBuffer.size()
|
|
&& memoryBuffer[pos] != 0 )
|
|
{
|
|
return memoryBuffer[pos];
|
|
}
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool TupleBuffer::SetTupleAtPos( const size_t pos, Tuple* t)
|
|
{
|
|
if( inMemory )
|
|
{
|
|
if(pos < memoryBuffer.size() )
|
|
{
|
|
memoryBuffer[pos] = t;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
GenericRelationIterator *TupleBuffer::MakeScan() const
|
|
{
|
|
return new TupleBufferIterator( *this );
|
|
}
|
|
|
|
GenericRelationIterator *TupleBuffer::MakeScan(TupleType* tt) const
|
|
{
|
|
return new TupleBufferIterator( *this, tt);
|
|
}
|
|
|
|
bool TupleBuffer::GetTupleFileStats( SmiStatResultType &result )
|
|
{
|
|
if( !inMemory && diskBuffer->GetTupleFileStats(result) ){
|
|
result.push_back(pair<string,string>("FilePurpose",
|
|
"TupleBufferTupleCoreFile"));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool TupleBuffer::GetLOBFileStats( SmiStatResultType &result )
|
|
{
|
|
if( !inMemory && diskBuffer->GetLOBFileStats(result) ){
|
|
result.push_back(pair<string,string>("FilePurpose",
|
|
"TupleBufferTupleLOBFile"));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
3.9 Class ~CircularTupleBuffer~
|
|
|
|
This class is used to collect tuples for sliding window operators. In this
|
|
persistent version, it is decided during the buffer construction, whether it
|
|
fits into main memory or it will be stored in disk.
|
|
|
|
*/
|
|
|
|
CircularTupleBuffer::CircularTupleBuffer(
|
|
bool _inMemory, TupleType* tT, unsigned int noTuples):
|
|
inMemory(_inMemory),
|
|
diskBuffer( 0 ),
|
|
overWrite(false),
|
|
maxNoTuples(noTuples),
|
|
totalExtSize( 0),
|
|
totalSize( 0 ),
|
|
totalMemSize( 0 )
|
|
{
|
|
if(! _inMemory)
|
|
{
|
|
diskBuffer = new Relation( tT, true );
|
|
overWrite= false;
|
|
}
|
|
}
|
|
/*
|
|
The constructor.
|
|
|
|
*/
|
|
|
|
CircularTupleBuffer::~CircularTupleBuffer()
|
|
{
|
|
clearMemory();
|
|
if( !inMemory ) {
|
|
diskBuffer->DeleteAndTruncate();
|
|
diskBuffer = 0;
|
|
}
|
|
}
|
|
|
|
void CircularTupleBuffer::clearMemory()
|
|
{
|
|
for( deque<Tuple*>::iterator it = memoryBuffer.begin();
|
|
it != memoryBuffer.end(); it++ )
|
|
{
|
|
//cout << (void*) *it << " - " << (*it)->GetNumOfRefs() << endl;
|
|
//if (*it != 0) {
|
|
(*it)->DeleteIfAllowed();
|
|
//}
|
|
}
|
|
memoryBuffer.clear();
|
|
totalSize=0;
|
|
}
|
|
|
|
/*
|
|
The destructor.
|
|
|
|
*/
|
|
|
|
int CircularTupleBuffer::GetNoTuples() const
|
|
{
|
|
if( inMemory )
|
|
return memoryBuffer.size();
|
|
else
|
|
return diskBuffer->GetNoTuples();
|
|
}
|
|
|
|
double CircularTupleBuffer::GetTotalRootSize() const
|
|
{
|
|
if( GetNoTuples() == 0 )
|
|
return 0;
|
|
|
|
if (inMemory)
|
|
return GetNoTuples() * memoryBuffer[0]->GetRootSize();
|
|
else
|
|
return diskBuffer->GetTupleType()->GetTotalSize();
|
|
}
|
|
|
|
|
|
double CircularTupleBuffer::GetTotalRootSize(int i) const
|
|
{
|
|
if( GetNoTuples() == 0 )
|
|
return 0;
|
|
|
|
if (inMemory)
|
|
return memoryBuffer[0]->GetRootSize(i);
|
|
else
|
|
return diskBuffer->GetTupleType()->GetTotalSize();
|
|
}
|
|
|
|
double CircularTupleBuffer::GetTotalExtSize() const
|
|
{
|
|
if( inMemory )
|
|
return totalExtSize;
|
|
else
|
|
return diskBuffer->GetTotalExtSize();
|
|
}
|
|
|
|
double CircularTupleBuffer::GetTotalExtSize(int i) const
|
|
{
|
|
if( GetNoTuples() == 0 )
|
|
return 0;
|
|
|
|
if( inMemory )
|
|
return memoryBuffer[0]->GetExtSize(i);
|
|
else
|
|
return diskBuffer->GetTotalExtSize(i);
|
|
}
|
|
|
|
|
|
double CircularTupleBuffer::GetTotalSize() const
|
|
{
|
|
if( inMemory )
|
|
return totalSize;
|
|
else
|
|
return diskBuffer->GetTotalSize();
|
|
}
|
|
|
|
double CircularTupleBuffer::GetTotalSize(int i) const
|
|
{
|
|
if( GetNoTuples() == 0 )
|
|
return 0;
|
|
|
|
if( inMemory )
|
|
return memoryBuffer[0]->GetSize(i);
|
|
else
|
|
return diskBuffer->GetTotalSize(i);
|
|
}
|
|
|
|
void CircularTupleBuffer::Clear()
|
|
{
|
|
if( inMemory )
|
|
{
|
|
clearMemory();
|
|
}
|
|
else
|
|
{
|
|
diskBuffer->DeleteAndTruncate();
|
|
diskBuffer = 0;
|
|
}
|
|
}
|
|
|
|
void CircularTupleBuffer::AppendTuple( Tuple *t )
|
|
{
|
|
if( inMemory )
|
|
{
|
|
t->IncReference();
|
|
if(memoryBuffer.size() < maxNoTuples)
|
|
memoryBuffer.push_back( t );
|
|
else
|
|
{
|
|
memoryBuffer.front()->DeleteIfAllowed();
|
|
memoryBuffer.pop_front();
|
|
memoryBuffer.push_back( t );
|
|
}
|
|
totalMemSize += t->GetMemSize();
|
|
totalSize += t->GetSize();
|
|
totalExtSize += t->GetExtSize();
|
|
|
|
}
|
|
else
|
|
{
|
|
// t->PinAttributes();
|
|
// if(! overWrite && (GetNoTuples() < maxNoTuples))
|
|
// diskBuffer->AppendTupleNoLOBs( t );
|
|
// else if(! overWrite && (GetNoTuples() == maxNoTuples))
|
|
// {
|
|
// overWrite= true;
|
|
// writeIterator= diskBuffer->MakeScan();
|
|
//// readIterator= diskBuffer->MakeScan();
|
|
//// readIterator->GetNextTuple();
|
|
// }
|
|
//
|
|
// if(overWrite)
|
|
// {
|
|
// //Advance the writeIterator
|
|
// Tuple* victim= writeIterator->GetNextTuple();
|
|
// if(writeIterator->EndOfScan())
|
|
// {
|
|
// delete writeIterator;
|
|
// writeIterator= diskBuffer->MakeScan();
|
|
// victim= writeIterator->GetNextTuple();
|
|
// }
|
|
// }
|
|
}
|
|
}
|
|
|
|
Tuple *CircularTupleBuffer::GetTuple( const TupleId& id,
|
|
const bool dontReportError ) const
|
|
{
|
|
if( inMemory )
|
|
{
|
|
Tuple* res = GetTupleAtPos( id );
|
|
res->IncReference();
|
|
return res;
|
|
}
|
|
else
|
|
{
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Tuple *CircularTupleBuffer::GetTuple( const TupleId& id,
|
|
const int attrIndex,
|
|
const vector< pair<int, int> >& intervals,
|
|
const bool dontReportError ) const
|
|
{
|
|
Tuple *t = 0;
|
|
if( (t = GetTuple( id, dontReportError )) != 0 )
|
|
t->GetAttribute( attrIndex )->Restrict( intervals );
|
|
return t;
|
|
}
|
|
|
|
Tuple* CircularTupleBuffer::GetTupleAtPos( const size_t pos ) const
|
|
{
|
|
if( inMemory )
|
|
{
|
|
if( pos < memoryBuffer.size()
|
|
&& memoryBuffer[pos] != 0 )
|
|
{
|
|
return memoryBuffer[pos];
|
|
}
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool CircularTupleBuffer::SetTupleAtPos( const size_t pos, Tuple* t)
|
|
{
|
|
if( inMemory )
|
|
{
|
|
if( pos < memoryBuffer.size() )
|
|
{
|
|
memoryBuffer[pos] = t;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
GenericRelationIterator *CircularTupleBuffer::MakeScan() const
|
|
{
|
|
return new CircularTupleBufferIterator( *this );
|
|
}
|
|
|
|
GenericRelationIterator *CircularTupleBuffer::MakeScan(TupleType* tt) const
|
|
{
|
|
return new CircularTupleBufferIterator( *this, tt);
|
|
}
|
|
|
|
bool CircularTupleBuffer::GetTupleFileStats( SmiStatResultType &result )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CircularTupleBuffer::GetLOBFileStats( SmiStatResultType &result )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CircularTupleBuffer::DeleteTuple(Tuple* t)
|
|
{
|
|
//Not implemented
|
|
return false;
|
|
}
|
|
/*
|
|
The function that deletes the given Tuple from the relation
|
|
|
|
*/
|
|
void CircularTupleBuffer::UpdateTuple( Tuple *tuple,
|
|
const vector<int>& changedIndices,
|
|
const vector<Attribute *>& newAttrs )
|
|
{
|
|
//Not implemented
|
|
bool Implemented= false;
|
|
assert(Implemented);
|
|
}
|
|
|
|
|
|
/*
|
|
3.9.3 ~TupleBufferIterator~
|
|
|
|
*/
|
|
TupleBufferIterator::TupleBufferIterator( const TupleBuffer& tupleBuffer ):
|
|
readData_Bytes( Counter::getRef(Symbol::CTR_TBUF_BYTES_R()) ),
|
|
readData_Pages( Counter::getRef(Symbol::CTR_TBUF_PAGES_R()) ),
|
|
tupleBuffer( tupleBuffer ),
|
|
currentTuple( 0 ),
|
|
diskIterator(
|
|
tupleBuffer.inMemory ?
|
|
0 :
|
|
tupleBuffer.diskBuffer->MakeScan() )
|
|
{}
|
|
|
|
TupleBufferIterator::TupleBufferIterator(
|
|
const TupleBuffer& tupleBuffer, TupleType* tt ):
|
|
readData_Bytes( Counter::getRef(Symbol::CTR_TBUF_BYTES_R()) ),
|
|
readData_Pages( Counter::getRef(Symbol::CTR_TBUF_PAGES_R()) ),
|
|
tupleBuffer( tupleBuffer ),
|
|
currentTuple( 0 ),
|
|
diskIterator(
|
|
tupleBuffer.inMemory ?
|
|
0 :
|
|
tupleBuffer.diskBuffer->MakeScan() ),
|
|
outtype( tt )
|
|
{}
|
|
/*
|
|
The constructor.
|
|
|
|
*/
|
|
TupleBufferIterator::~TupleBufferIterator()
|
|
{
|
|
readData_Pages = readData_Bytes / WinUnix::getPageSize();
|
|
delete diskIterator;
|
|
}
|
|
/*
|
|
The destructor.
|
|
|
|
*/
|
|
|
|
Tuple *TupleBufferIterator::GetNextTuple()
|
|
{
|
|
if( diskIterator )
|
|
{
|
|
Tuple* t = diskIterator->GetNextTuple();
|
|
if (t)
|
|
readData_Bytes += t->GetExtSize();
|
|
return t;
|
|
}
|
|
else
|
|
{
|
|
if( currentTuple == tupleBuffer.memoryBuffer.size() )
|
|
return 0;
|
|
Tuple *result =
|
|
tupleBuffer.memoryBuffer[currentTuple];
|
|
result->IncReference();
|
|
currentTuple++;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
Tuple *TupleBufferIterator::GetNextTuple(const list<int>& attrList)
|
|
{
|
|
if( diskIterator )
|
|
{
|
|
Tuple* t = diskIterator->GetNextTuple(attrList);
|
|
if (t)
|
|
readData_Bytes += t->GetExtSize();
|
|
return t;
|
|
}
|
|
else
|
|
{
|
|
if( currentTuple == tupleBuffer.memoryBuffer.size() )
|
|
return 0;
|
|
|
|
Tuple *t =
|
|
tupleBuffer.memoryBuffer[currentTuple];
|
|
|
|
Tuple *result = new Tuple( outtype );
|
|
list<int>::const_iterator iter = attrList.begin();
|
|
for(int i=0 ; iter != attrList.end(); ++iter, ++i )
|
|
result->CopyAttribute(*iter, t, i);
|
|
currentTuple++;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
TupleId TupleBufferIterator::GetTupleId() const
|
|
{
|
|
if( diskIterator )
|
|
{
|
|
return diskIterator->GetTupleId();
|
|
}
|
|
else
|
|
{
|
|
return currentTuple-1;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
3.9.3 ~CircularTupleBufferIterator~
|
|
|
|
*/
|
|
CircularTupleBufferIterator::CircularTupleBufferIterator(
|
|
const CircularTupleBuffer& _tupleBuffer ):
|
|
tupleBuffer( _tupleBuffer )
|
|
{
|
|
currentTuple= tupleBuffer.memoryBuffer.begin() ;
|
|
}
|
|
|
|
CircularTupleBufferIterator::CircularTupleBufferIterator(
|
|
const CircularTupleBuffer& _tupleBuffer, TupleType* tt ):
|
|
tupleBuffer( _tupleBuffer ),
|
|
outtype( tt )
|
|
{
|
|
currentTuple= tupleBuffer.memoryBuffer.begin();
|
|
}
|
|
/*
|
|
The constructor.
|
|
|
|
*/
|
|
CircularTupleBufferIterator::~CircularTupleBufferIterator()
|
|
{
|
|
|
|
}
|
|
/*
|
|
The destructor.
|
|
|
|
*/
|
|
|
|
Tuple *CircularTupleBufferIterator::GetNextTuple()
|
|
{
|
|
if( currentTuple == tupleBuffer.memoryBuffer.end() )
|
|
return 0;
|
|
|
|
Tuple *result = *currentTuple;
|
|
result->IncReference();
|
|
++currentTuple;
|
|
return result;
|
|
}
|
|
|
|
Tuple *CircularTupleBufferIterator::GetNextTuple(const list<int>& attrList)
|
|
{
|
|
if( currentTuple == tupleBuffer.memoryBuffer.end() )
|
|
return 0;
|
|
|
|
Tuple *t = *currentTuple;
|
|
|
|
Tuple *result = new Tuple( outtype );
|
|
list<int>::const_iterator iter = attrList.begin();
|
|
for(int i=0 ; iter != attrList.end(); ++iter, ++i )
|
|
result->CopyAttribute(*iter, t, i);
|
|
++currentTuple;
|
|
return result;
|
|
}
|
|
|
|
TupleId CircularTupleBufferIterator::GetTupleId() const
|
|
{
|
|
return (*currentTuple)->GetTupleId();
|
|
}
|
|
|
|
|
|
RandomTBuf::RandomTBuf(size_t setSize /*= default*/)
|
|
: subsetSize(setSize),
|
|
streamPos(0),
|
|
run(0),
|
|
trace(false),
|
|
memBuf(subsetSize,0)
|
|
{}
|
|
|
|
|
|
Tuple* RandomTBuf::ReplacedByRandom(Tuple* s, size_t& i, bool& replaced)
|
|
{
|
|
Tuple* t = 0;
|
|
replaced = false;
|
|
|
|
i = streamPos % subsetSize;
|
|
if ( i == 0 )
|
|
{
|
|
run++;
|
|
|
|
if (trace) {
|
|
|
|
cerr << endl
|
|
<< "subsetSize: " << subsetSize
|
|
<< ", run: " << run
|
|
<< ", replaced slots:"
|
|
<< endl;
|
|
}
|
|
}
|
|
|
|
if (run > 0)
|
|
{
|
|
int r = WinUnix::rand(run);
|
|
if (trace) {
|
|
cerr << ", r = " << r;
|
|
}
|
|
|
|
assert( (r >= 1) && (r <= run) );
|
|
|
|
//cout << "s:" << *s << endl;
|
|
if ( r == run )
|
|
{
|
|
if (trace) {
|
|
cerr << ", i = " << i;
|
|
}
|
|
|
|
// tuple s will be selected for the front part
|
|
// of the output stream. The currently stored tuple
|
|
// t at slot i will be released.
|
|
replaced = true;
|
|
|
|
t = memBuf[i];
|
|
if (t != 0) {
|
|
//cout << "t:" << *t << endl;
|
|
t->DeleteIfAllowed();
|
|
}
|
|
s->IncReference();
|
|
memBuf[i] = s;
|
|
}
|
|
} // end of run > 0
|
|
|
|
streamPos++;
|
|
return t;
|
|
}
|
|
|
|
void RandomTBuf::copy2TupleBuf(TupleBuffer& tb)
|
|
{
|
|
for(iterator it = begin(); it != end(); it++) {
|
|
if (*it != 0) {
|
|
(*it)->DeleteIfAllowed();
|
|
tb.AppendTuple(*it);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
4 Type constructor ~rel~
|
|
|
|
4.2 Implementation of the class ~Relation~
|
|
|
|
This class implements the persistent representation of the type
|
|
constructor ~rel~.
|
|
|
|
*/
|
|
map<SmiFileId,Relation*> Relation::pointerTable;
|
|
|
|
|
|
void
|
|
Relation::InitFiles( bool open /*= false */) {
|
|
|
|
bool rc = false;
|
|
if (open) {
|
|
rc = tupleFile.Open(relDesc.tupleFileId);
|
|
} else {
|
|
rc = tupleFile.Create();
|
|
}
|
|
|
|
if( !rc )
|
|
{
|
|
string error;
|
|
SmiEnvironment::GetLastErrorCode( error );
|
|
cerr << error << endl;
|
|
assert( false );
|
|
}
|
|
relDesc.tupleFileId = tupleFile.GetFileId();
|
|
|
|
|
|
if( pointerTable.find( relDesc.tupleFileId ) ==
|
|
pointerTable.end() )
|
|
pointerTable.insert( make_pair( relDesc.tupleFileId,
|
|
this ) );
|
|
|
|
// init LOB File
|
|
if (relDesc.tupleType->NumOfFlobs() > 0 && relDesc.lobFileId == 0)
|
|
{
|
|
SmiRecordFile rf(false,0, relDesc.isTemp);
|
|
if(!rf.Create()){
|
|
assert(false);
|
|
}
|
|
relDesc.lobFileId = rf.GetFileId();
|
|
rf.Close();
|
|
}
|
|
}
|
|
|
|
|
|
Relation::Relation( const ListExpr typeInfo, bool isTemp ):
|
|
relDesc( typeInfo, isTemp ),
|
|
tupleFile( false, 0, isTemp )
|
|
{
|
|
Relation::InitFiles();
|
|
}
|
|
|
|
Relation::Relation( TupleType *tupleType, bool isTemp ):
|
|
relDesc( tupleType, isTemp ),
|
|
tupleFile( false, 0, isTemp )
|
|
{
|
|
Relation::InitFiles();
|
|
}
|
|
|
|
Relation::Relation( const RelationDescriptor& relDesc ):
|
|
relDesc( relDesc ),
|
|
tupleFile( false, 0, relDesc.isTemp )
|
|
{
|
|
Relation::InitFiles(true);
|
|
}
|
|
|
|
Relation::~Relation()
|
|
{
|
|
//delete privateRelation;
|
|
tupleFile.Close();
|
|
}
|
|
|
|
|
|
Relation *
|
|
Relation::RestoreFromList( ListExpr typeInfo, ListExpr value,
|
|
int errorPos, ListExpr& errorInfo,
|
|
bool& correct )
|
|
{
|
|
ListExpr rest = value;
|
|
for( int i = 0; i < 3; i++ )
|
|
rest = nl->Rest( rest );
|
|
|
|
int n = (nl->ListLength( rest )-2)/2;
|
|
vector<double> attrExtSize( n ),
|
|
attrSize( n );
|
|
|
|
for( int i = 0; i < n; i++ )
|
|
{
|
|
attrExtSize[i] = nl->RealValue( nl->First( rest ) );
|
|
attrSize[i] = nl->RealValue( nl->Second( rest ) );
|
|
rest = nl->Rest( rest );
|
|
rest = nl->Rest( rest );
|
|
}
|
|
|
|
RelationDescriptor relDesc( typeInfo,
|
|
nl->IntValue( nl->First( value ) ),
|
|
nl->RealValue( nl->Second( value ) ),
|
|
nl->RealValue( nl->Third( value ) ),
|
|
attrExtSize, attrSize,
|
|
nl->IntValue( nl->First( rest ) ),
|
|
nl->IntValue( nl->Second( rest ) ) );
|
|
|
|
return new Relation( relDesc );
|
|
}
|
|
|
|
ListExpr
|
|
Relation::SaveToList( ListExpr typeInfo )
|
|
{
|
|
ListExpr result =
|
|
nl->OneElemList( nl->IntAtom( relDesc.noTuples ) ),
|
|
last = result;
|
|
|
|
last =
|
|
nl->Append( last, nl->RealAtom( relDesc.totalExtSize ) );
|
|
last =
|
|
nl->Append( last, nl->RealAtom( relDesc.totalSize ) );
|
|
|
|
for( int i = 0;
|
|
i < relDesc.tupleType->GetNoAttributes();
|
|
i++ )
|
|
{
|
|
last =
|
|
nl->Append( last,
|
|
nl->RealAtom( relDesc.attrExtSize[i] ) );
|
|
last =
|
|
nl->Append( last,
|
|
nl->RealAtom( relDesc.attrSize[i] ) );
|
|
}
|
|
|
|
last =
|
|
nl->Append( last, nl->IntAtom( relDesc.tupleFileId ) );
|
|
last =
|
|
nl->Append( last, nl->IntAtom( relDesc.lobFileId ) );
|
|
|
|
return result;
|
|
}
|
|
|
|
Relation *
|
|
Relation::Open( SmiRecord& valueRecord, size_t& offset,
|
|
const ListExpr typeInfo )
|
|
{
|
|
RelationDescriptor relDesc( typeInfo );
|
|
|
|
valueRecord.SetPos(offset);
|
|
valueRecord.Read( relDesc.noTuples );
|
|
valueRecord.Read( relDesc.totalExtSize );
|
|
valueRecord.Read( relDesc.totalSize );
|
|
|
|
for( int i = 0; i < relDesc.tupleType->GetNoAttributes(); i++ )
|
|
{
|
|
double d;
|
|
valueRecord.Read( d );
|
|
relDesc.attrExtSize[i] = d;
|
|
}
|
|
|
|
for( int i = 0; i < relDesc.tupleType->GetNoAttributes(); i++ )
|
|
{
|
|
double d;
|
|
valueRecord.Read( d );
|
|
relDesc.attrSize[i] = d;
|
|
}
|
|
|
|
valueRecord.Read( relDesc.tupleFileId );
|
|
valueRecord.Read( relDesc.lobFileId );
|
|
|
|
offset = valueRecord.GetPos();
|
|
|
|
return new Relation( relDesc );
|
|
}
|
|
|
|
bool
|
|
Relation::Save( SmiRecord& valueRecord, size_t& offset,
|
|
const ListExpr typeInfo )
|
|
{
|
|
valueRecord.SetPos(offset);
|
|
valueRecord.Write( relDesc.noTuples);
|
|
valueRecord.Write( relDesc.totalExtSize );
|
|
valueRecord.Write( relDesc.totalSize );
|
|
|
|
for( int i = 0; i < relDesc.tupleType->GetNoAttributes(); i++ )
|
|
{
|
|
valueRecord.Write( relDesc.attrExtSize[i] );
|
|
}
|
|
|
|
for( int i = 0; i < relDesc.tupleType->GetNoAttributes(); i++ )
|
|
{
|
|
valueRecord.Write( relDesc.attrSize[i] );
|
|
}
|
|
|
|
valueRecord.Write( relDesc.tupleFileId );
|
|
valueRecord.Write( relDesc.lobFileId );
|
|
|
|
offset = valueRecord.GetPos();
|
|
return true;
|
|
}
|
|
|
|
void Relation::ErasePointer()
|
|
{
|
|
if( pointerTable.find( relDesc.tupleFileId ) !=
|
|
pointerTable.end() )
|
|
{
|
|
pointerTable.erase( relDesc.tupleFileId );
|
|
} else {
|
|
map<SmiFileId, Relation*>::iterator it;
|
|
for (it=pointerTable.begin(); it != pointerTable.end(); it++) {
|
|
//cout << "ErasePointer hotfix" << endl;
|
|
if(it->second == this){
|
|
pointerTable.erase(it);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void Relation::Close()
|
|
{
|
|
ErasePointer();
|
|
delete this;
|
|
}
|
|
|
|
void Relation::Delete(bool shouldDeleteOnlyPersistentFiles /* = false*/)
|
|
{
|
|
tupleFile.Close();
|
|
tupleFile.Drop();
|
|
if(relDesc.lobFileId){
|
|
char mode = relDesc.isTemp?1:0;
|
|
Flob::dropFile(relDesc.lobFileId, mode);
|
|
SmiRecordFile rf(false,0, relDesc.isTemp);
|
|
rf.Open(relDesc.lobFileId);
|
|
rf.Close();
|
|
rf.Drop();
|
|
}
|
|
ErasePointer();
|
|
|
|
if(!shouldDeleteOnlyPersistentFiles) delete this;
|
|
}
|
|
|
|
void Relation::DeleteAndTruncate()
|
|
{
|
|
tupleFile.Remove();
|
|
tupleFile.Drop();
|
|
if(relDesc.lobFileId){
|
|
char mode = relDesc.isTemp?1:0;
|
|
Flob::dropFile(relDesc.lobFileId, mode);
|
|
SmiRecordFile rf(false,0, relDesc.isTemp);
|
|
rf.Open(relDesc.lobFileId);
|
|
rf.Close();
|
|
rf.Remove();
|
|
rf.Drop();
|
|
}
|
|
// else {
|
|
// cerr << "Relation has no LOB-file!" << endl;
|
|
// }
|
|
|
|
ErasePointer();
|
|
|
|
delete this;
|
|
}
|
|
|
|
|
|
|
|
Relation *Relation::Clone() const
|
|
{
|
|
Relation *r = new Relation( relDesc.tupleType );
|
|
|
|
Tuple *t;
|
|
GenericRelationIterator *iter = MakeScan();
|
|
while( (t = iter->GetNextTuple()) != 0 )
|
|
{
|
|
r->AppendTuple( t );
|
|
t->DeleteIfAllowed();
|
|
}
|
|
delete iter;
|
|
|
|
return r;
|
|
}
|
|
|
|
void Relation::AppendTuple( Tuple *tuple )
|
|
{
|
|
tuple->Save( &tupleFile,
|
|
relDesc.lobFileId,
|
|
relDesc.totalExtSize,
|
|
relDesc.totalSize,
|
|
relDesc.attrExtSize,
|
|
relDesc.attrSize );
|
|
|
|
relDesc.noTuples += 1;
|
|
}
|
|
|
|
|
|
void Relation::AppendTupleNoLOBs( Tuple *tuple )
|
|
{
|
|
tuple->Save( &tupleFile,
|
|
relDesc.lobFileId,
|
|
relDesc.totalExtSize,
|
|
relDesc.totalSize,
|
|
relDesc.attrExtSize,
|
|
relDesc.attrSize, true );
|
|
|
|
relDesc.noTuples += 1;
|
|
}
|
|
|
|
void Relation::Clear()
|
|
{
|
|
relDesc.noTuples = 0;
|
|
relDesc.totalExtSize = 0;
|
|
relDesc.totalSize = 0;
|
|
if(tupleFile.IsTemp()){
|
|
tupleFile.ReCreate();
|
|
} else {
|
|
tupleFile.Truncate();
|
|
}
|
|
if(relDesc.lobFileId){
|
|
SmiRecordFile rf(false,0, relDesc.isTemp);
|
|
rf.Open(relDesc.lobFileId);
|
|
rf.Truncate();
|
|
rf.Close();
|
|
}
|
|
}
|
|
|
|
Tuple *Relation::GetTuple( const TupleId& id,
|
|
const bool dontReportError ) const
|
|
{
|
|
Tuple *result = new Tuple( relDesc.tupleType );
|
|
if( result->Open( &tupleFile, relDesc.lobFileId, id, dontReportError ) )
|
|
return result;
|
|
|
|
delete result;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
The next fcuntion updates the tuple by deleting the old attributes at the
|
|
positions given by 'changedIndices' and puts the new attributres
|
|
from 'newAttrs' into their places. These changes are made persistent.
|
|
|
|
*/
|
|
void Relation::UpdateTuple( Tuple *tuple,
|
|
const vector<int>& changedIndices,
|
|
const vector<Attribute *>& newAttrs )
|
|
{
|
|
tuple->UpdateAttributes(
|
|
changedIndices, newAttrs,
|
|
relDesc.totalExtSize,
|
|
relDesc.totalSize,
|
|
relDesc.attrExtSize,
|
|
relDesc.attrSize );
|
|
}
|
|
|
|
/*
|
|
Deletes the tuple from the relation, Flobs and SMIRecord are deleted
|
|
and the size of the relation is adjusted.
|
|
|
|
*/
|
|
bool Relation::DeleteTuple( Tuple *tuple )
|
|
{
|
|
if( tupleFile.DeleteRecord( tuple->GetTupleId() ) )
|
|
{
|
|
Attribute* nextAttr = 0;
|
|
Flob* nextFlob = 0;
|
|
|
|
relDesc.noTuples -= 1;
|
|
relDesc.totalExtSize -= tuple->GetRootSize();
|
|
relDesc.totalSize -= tuple->GetRootSize();
|
|
|
|
for (int i = 0; i < tuple->GetNoAttributes(); i++)
|
|
{
|
|
nextAttr = tuple->GetAttribute(i);
|
|
nextAttr->Finalize();
|
|
for (int j = 0; j < nextAttr->NumOfFLOBs(); j++)
|
|
{
|
|
nextFlob = nextAttr->GetFLOB(j);
|
|
SmiSize fsz = nextFlob->getSize();
|
|
|
|
assert( i >= 0 &&
|
|
(size_t)i < relDesc.attrSize.size() );
|
|
relDesc.attrSize[i] -= fsz;
|
|
relDesc.totalSize -= fsz;
|
|
|
|
if( fsz < Tuple::extensionLimit )
|
|
{
|
|
assert( i >= 0 &&
|
|
(size_t)i < relDesc.attrExtSize.size() );
|
|
relDesc.attrExtSize[i] -= fsz;
|
|
relDesc.totalExtSize -= fsz;
|
|
}
|
|
|
|
nextFlob->destroy();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int Relation::GetNoTuples() const
|
|
{
|
|
return relDesc.noTuples;
|
|
}
|
|
|
|
TupleType *Relation::GetTupleType() const
|
|
{
|
|
return relDesc.tupleType;
|
|
}
|
|
|
|
double Relation::GetTotalRootSize() const
|
|
{
|
|
return relDesc.noTuples *
|
|
relDesc.tupleType->GetCoreSize();
|
|
}
|
|
|
|
double Relation::GetTotalRootSize( int i ) const
|
|
{
|
|
return relDesc.noTuples *
|
|
relDesc.tupleType->GetAttributeType(i).coreSize;
|
|
}
|
|
|
|
double Relation::GetTotalExtSize() const
|
|
{
|
|
return relDesc.totalExtSize;
|
|
}
|
|
|
|
double Relation::GetTotalExtSize( int i ) const
|
|
{
|
|
assert( i >= 0 &&
|
|
(size_t)i < relDesc.attrExtSize.size() );
|
|
return relDesc.attrExtSize[i];
|
|
}
|
|
|
|
double Relation::GetTotalSize() const
|
|
{
|
|
return relDesc.totalSize;
|
|
}
|
|
|
|
double Relation::GetTotalSize( int i ) const
|
|
{
|
|
assert( i >= 0 &&
|
|
(size_t)i < relDesc.attrSize.size() );
|
|
return relDesc.attrSize[i];
|
|
}
|
|
|
|
GenericRelationIterator *Relation::MakeScan() const
|
|
{
|
|
return new RelationIterator( *this );
|
|
}
|
|
|
|
GenericRelationIterator *Relation::MakeScan(TupleType* tt) const
|
|
{
|
|
return new RelationIterator( *this, tt );
|
|
}
|
|
|
|
|
|
|
|
RandomRelationIterator *Relation::MakeRandomScan() const
|
|
{
|
|
return new RandomRelationIterator( *this );
|
|
}
|
|
|
|
|
|
bool Relation::GetTupleFileStats( SmiStatResultType &result )
|
|
{
|
|
result = tupleFile.GetFileStatistics(SMI_STATS_EAGER);
|
|
std::stringstream fileid;
|
|
fileid << tupleFile.GetFileId();
|
|
result.push_back(pair<string,string>("FilePurpose",
|
|
"RelationTupleCoreFile"));
|
|
result.push_back(pair<string,string>("FileId",fileid.str()));
|
|
return true;
|
|
}
|
|
|
|
|
|
bool Relation::GetLOBFileStats( SmiStatResultType &result )
|
|
{
|
|
// cerr << "Relation::GetLOBFileStats( SmiStatResultType &result ) still "
|
|
// << "lacks implementation!" << endl;
|
|
SmiFileId lobFileId = relDesc.lobFileId;
|
|
SmiRecordFile lobFile(false);
|
|
if( lobFileId == 0 ){
|
|
return true;
|
|
} else if( !lobFile.Open( lobFileId ) ){
|
|
return false;
|
|
} else {
|
|
result = lobFile.GetFileStatistics(SMI_STATS_EAGER);
|
|
result.push_back(pair<string,string>("FilePurpose",
|
|
"RelationTupleLOBFile"));
|
|
std::stringstream fileid;
|
|
fileid << lobFileId;
|
|
result.push_back(pair<string,string>("FileId",fileid.str()));
|
|
};
|
|
if( !lobFile.Close() ){
|
|
return false;
|
|
};
|
|
return true;
|
|
}
|
|
|
|
|
|
// SPM: Extension communicated by K. Teufel
|
|
|
|
Relation *Relation::GetRelation (const SmiFileId fileId )
|
|
{
|
|
map<SmiFileId, Relation*>::iterator it =
|
|
pointerTable.find(fileId);
|
|
if(it!=pointerTable.end()){
|
|
return it->second;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
4.4 Implementation of the class ~RelationIterator~
|
|
(using ~PrefetchingIterator~)
|
|
|
|
This class is used for scanning (iterating through) relations.
|
|
|
|
*/
|
|
RelationIterator::RelationIterator( const Relation& rel, TupleType* tt /*=0*/ ):
|
|
iterator( rel.tupleFile.SelectAllPrefetched() ),
|
|
relation( rel ),
|
|
endOfScan( false ),
|
|
currentTupleId( -1 ),
|
|
outtype( tt )
|
|
{}
|
|
|
|
|
|
|
|
RelationIterator::~RelationIterator()
|
|
{
|
|
delete iterator;
|
|
}
|
|
|
|
|
|
Tuple* RelationIterator::GetNextTuple()
|
|
{
|
|
//#define TRACE_ON
|
|
// NTRACE(10000, "GetNextTuple()")
|
|
//#undef TRACE_ON
|
|
if( !iterator->Next() )
|
|
{
|
|
endOfScan = true;
|
|
currentTupleId = -1;
|
|
return 0;
|
|
}
|
|
|
|
Tuple *result = new Tuple( relation.relDesc.tupleType );
|
|
|
|
bool openOK = result->Open( &relation.tupleFile,
|
|
relation.relDesc.lobFileId,
|
|
iterator );
|
|
assert(openOK); // otherwise the prefetching iterators works wrong
|
|
|
|
currentTupleId = result->GetTupleId();
|
|
return result;
|
|
}
|
|
|
|
Tuple* RelationIterator::GetNthTuple(const size_t n, const bool random){
|
|
if(endOfScan){
|
|
return 0;
|
|
}
|
|
assert(n>0);
|
|
Tuple* result = 0;
|
|
size_t num = random?rand()%n:n-1;
|
|
for(size_t i=0;i<n && !endOfScan ;i++){
|
|
if(!iterator->Next()){
|
|
endOfScan = true;
|
|
} else {
|
|
if(i==num){
|
|
result = new Tuple( relation.relDesc.tupleType );
|
|
bool openOK = result->Open( &relation.tupleFile,
|
|
relation.relDesc.lobFileId,
|
|
iterator );
|
|
assert(openOK); // otherwise the prefetching iterators works wrong
|
|
}
|
|
}
|
|
}
|
|
currentTupleId = result?result->GetTupleId():-1;
|
|
return result;
|
|
}
|
|
|
|
|
|
Tuple* RelationIterator::GetNextTuple( const list<int>& attrList )
|
|
{
|
|
//#define TRACE_ON
|
|
// NTRACE(10000, "GetNextTuple()")
|
|
//#undef TRACE_ON
|
|
if( !iterator->Next() )
|
|
{
|
|
endOfScan = true;
|
|
currentTupleId = -1;
|
|
return 0;
|
|
}
|
|
|
|
Tuple* result = 0;
|
|
|
|
/*
|
|
if (outtype) {
|
|
new Tuple( outtype );
|
|
} else {
|
|
new Tuple( relation.relDesc.tupleType );
|
|
} */
|
|
|
|
SHOW( *relation.relDesc.tupleType )
|
|
SHOW( *outtype )
|
|
|
|
result = new Tuple( relation.relDesc.tupleType );
|
|
|
|
|
|
|
|
result->OpenPartial( outtype, attrList,
|
|
&relation.tupleFile,
|
|
relation.relDesc.lobFileId,
|
|
iterator );
|
|
|
|
currentTupleId = result->GetTupleId();
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
TupleId RelationIterator::GetTupleId() const
|
|
{
|
|
return currentTupleId;
|
|
}
|
|
|
|
bool RelationIterator::EndOfScan() const
|
|
{
|
|
return endOfScan;
|
|
}
|
|
|
|
/*
|
|
4.5 Implementation of the class ~RandomRelationIterator~
|
|
(using ~PrefetchingIterator~)
|
|
|
|
This class is used for scanning (iterating through) relations. Currently, the
|
|
random iteration is only helpful for creating samples since it is possible to
|
|
skip some of the tuples which makes the iteration a litte bit more efficent.
|
|
Here we need an implementation which is able to skip some pages of the
|
|
underlying record file. This would make it rather efficient,
|
|
|
|
*/
|
|
RandomRelationIterator::RandomRelationIterator( const Relation& relation ):
|
|
RelationIterator( relation )
|
|
{}
|
|
|
|
RandomRelationIterator::~RandomRelationIterator()
|
|
{}
|
|
|
|
Tuple* RandomRelationIterator::GetNextTuple(int step/*=1*/)
|
|
{
|
|
//#define TRACE_ON
|
|
// NTRACE(10000, "GetNextTuple()")
|
|
//#undef TRACE_ON
|
|
for (; step > 0; step--) { // advance the iterator #step times
|
|
if( !iterator->Next() )
|
|
{
|
|
endOfScan = true;
|
|
currentTupleId = -1;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
Tuple *result = new Tuple( relation.relDesc.tupleType );
|
|
|
|
result->Open( &relation.tupleFile,
|
|
relation.relDesc.lobFileId,
|
|
iterator );
|
|
|
|
currentTupleId = result->GetTupleId();
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
5 Auxiliary functions
|
|
|
|
5.1 Function ~Concat~
|
|
|
|
Copies the attribute values of two tuples
|
|
(words) ~r~ and ~s~ into tuple (word) ~t~.
|
|
|
|
*/
|
|
void Concat( Tuple *r, Tuple *s, Tuple *t )
|
|
{
|
|
int rnoattrs = r->GetNoAttributes();
|
|
int snoattrs = s->GetNoAttributes();
|
|
//int tnoattrs = rnoattrs + snoattrs;
|
|
|
|
for( int i = 0; i < rnoattrs; i++) {
|
|
t->CopyAttribute( i, r, i );
|
|
}
|
|
for (int j = 0; j < snoattrs; j++) {
|
|
t->CopyAttribute( j, s, rnoattrs + j );
|
|
}
|
|
}
|
|
|