blob: 64fb0414c1a2b743520b48196f86be50b24a750f [file] [log] [blame]
/**********************************************************************
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// @@@ END COPYRIGHT @@@
**********************************************************************/
/* -*-C++-*-
****************************************************************************
*
* File: NAVersionedObject.cpp
*
* Description: The NAVersionedObject class encapsulates the version header
* added to each SQL Executor Plan object stored on disk. It
* is the base class for the classes these objects belong. It
* also provides driver routines for the packing and unpacking
* processes on those objects.
*
* During packing, the objects are converted to a predefined
* reference layout and pointers in the objects are converted
* into offsets. During unpacking, object images are modified
* to fit in the local platform; older objects are migrated to
* their current versions.
*
* The NAVersionedObjectPtr class encapsulate a pointer to a
* NAVersionedObject. The class is packed with fillers on a
* 32-bit platform so that the class size is always 64 bits.
* This is done to smoothen transition to 64-bit platforms in
* the future. Objects of the class are used inside all plan
* objects derived from NAVersionedObject, so that these plan
* objects will have the same size on 64-bit platforms.
*
* Created: 9/8/98
* Language: C++
* Status: $State: Exp $
*
*
*
****************************************************************************
*/
#include "Platform.h"
#include "NAVersionedObject.h"
// =====================================================================
// Function bodies of class NAVersionedObject
// =====================================================================
// ---------------------------------------------------------------------
// Constructor
//
// Note that imageSize_ and versionIDArray_ are only filled in at
// packing time. These values are not useful on the source platform
// where the objects are constructed. They are only useful at the
// destination while the objects are unpacked. Before the objects are
// packed, drivePack() will make sure their values are correctly set.
// This arrangement avoids the dependence on the constructors of sub-
// classes of NAVersionedObject on setting these values correctly.
// ---------------------------------------------------------------------
NAVersionedObject::NAVersionedObject(Int16 classID)
: classID_(classID),
reallocatedAddress_(NULL),
imageSize_(0)
{
clearFillers();
initFlags(); // set to state of "not packed".
clearVersionIDArray();
str_cpy_all(eyeCatcher_,VOBJ_EYE_CATCHER,VOBJ_EYE_CATCHER_SIZE);
}
// ---------------------------------------------------------------------
// All subclasses could redefine convertToReference/LocalPlatform() to
// convert their members to the reference platform from the local
// platform and vice versa. Typically, this only involves toggling the
// endianness of some members.
// ---------------------------------------------------------------------
void NAVersionedObject::convertToReferencePlatform()
{
#ifndef NA_LITTLE_ENDIAN
toggleEndianness();
#endif
}
void NAVersionedObject::convertToLocalPlatform()
{
#ifndef NA_LITTLE_ENDIAN
toggleEndianness();
#endif
}
// ---------------------------------------------------------------------
// reallocateImage() provides the basic implementation for the virtual
// function migrateToNewVersion(). It is called when the new version
// object has a larger size than the older version object image we
// have. A new object is allocated. The old image is overlay onto it.
// The left-over space will be zero'ed. Finally, reallocatedAddress_
// field in the older object is set to the address of the new object.
// ---------------------------------------------------------------------
NAVersionedObject *NAVersionedObject::reallocateImage(void * reallocator)
{
Space *space = (Space *)reallocator;
short size = getClassSize();
char *newObjPtr = (char *)( (space == NULL) ?
(::operator new(size)) :
(space->allocateAlignedSpace(size)) );
str_pad(newObjPtr,size,0);
str_cpy_all(newObjPtr,(char *)this,imageSize_);
setReallocatedAddress((NAVersionedObject *)newObjPtr);
((NAVersionedObject *)(newObjPtr))->setImageSize(size);
return (NAVersionedObject *)(newObjPtr);
}
// ---------------------------------------------------------------------
// This is a utility for use by redefined migrateToNewVersion() at the
// subclass level. Given the old class size and the new class size, it
// expands the room for members of a particular subclass inside an image
// of possibly another subclass down the derivation chain. It assumes
// the image has been reallocated so that it is big enough to make this
// expansion.
// ---------------------------------------------------------------------
void NAVersionedObject::makeRoomForNewVersion(
short oldSubClassSize,
short newSubClassSize)
{
// -----------------------------------------------------------------
// This is an intermediate subclass in derivation chain which needs
// expansion. Shift all members of subclass(es) lower in the deri-
// vation chain downwards to make room.
// -----------------------------------------------------------------
if (imageSize_ != newSubClassSize)
{
char *src = (char *)this + oldSubClassSize;
char *des = (char *)this + newSubClassSize;
short siz = imageSize_ - newSubClassSize;
str_cpy_all(des,src,siz);
}
}
// ---------------------------------------------------------------------
// Subclasses could redefine migrateToNewVersion() when a new version
// is introduced according to the following template:
//
// long SubClass::migrateToNewVersion(NAVersionedObject *&newImage)
// {
// if (newImage == NULL)
// {
// newImage = ( getImageSize() == getClassSize() ?
// this :
// reallocateNewImage() );
// }
//
// // Version not supported when migrating base class.
// if (BaseClass::migrateToNewVersion(newImage)) return -1;
//
// // ?n is the current version of SubClass. ?cs1 is the class size
// // of version 1, ?cs2 is the class size of version 2, ..., etc.
// //
// const short classSizesArray[?n] = { ?cs1, ?cs2, ..., ?csn };
//
// short newClassSize = SubClass::getClassSize(); // or ?csn
//
// // ?SUBCLASS_LEVEL begins with 0 if SubClass is directly derived
// // from NAVersionedObject and increases down the derivation chain.
// //
// unsigned char version = getImageVersionID(?SUBCLASS_LEVEL);
// short oldClassSize = classSizesArray[version];
//
// if (oldClassSize != newClassSize)
// makeRoomForNewVersion(oldClassSize,newClassSize);
//
// // Implement migration of old members other than size difference OR
// // return -1 if it was decided that a particular version shouldn't
// // be supported anymore. Note that new members should be initialized
// // only later at initNewMembers().
// //
// switch (version)
// {
// // provides code to migrate image from version 1 to 2.
// case 1:
// // provides code to migrate image from version 2 to 3.
// case 2:
// // ... upto version ?(n-1) to ?n.
// };
//
// return 0;
// }
//
// This method is redefined by following a strategy similar to RelExpr::
// copyTopNode() in the optimizer directory. Each subclass invokes the
// same method on its base class and then handles the migration of its
// own members. The object is reallocated if needed at the "real" sub-
// class the object belongs.
//
// A return code of -1 is used to indicate that the object has a version
// which is not supported anymore. Also notice that the versionIDArray_
// should only be updated at initNewMembers().
// ---------------------------------------------------------------------
Lng32 NAVersionedObject::migrateToNewVersion(
NAVersionedObject *&newImage)
{
short tempimagesize = getClassSize();
// -----------------------------------------------------------------
// The base class implementation of migrateToNewVersion() is only
// called with newImage == NULL when the same function is not
// redefined at the subclass. That means no new version of that
// subclass has been invented yet.
// -----------------------------------------------------------------
if (newImage == NULL)
{
assert(imageSize_ == getClassSize());
// ---------------------------------------------------------------
// Should also assert this->versionIDArray_ is same as the one
// generated from populateVersionIDArray(). But it takes too much
// time.
// ---------------------------------------------------------------
// !!! -----------------------------------------------------------
// CODE SPECIFIC TO FIRST RELEASE: to be removed at the second
// release : to guard against the case where a 1st release exec-
// utor is used to execute 2nd release plan. A more general mech-
// anism should be developed at the 2nd release to guard against
// this case.
// ---------------------------------------------------------------
unsigned char * versionIDArray = getImageVersionIDArray();
for (Int32 i = 0; i < VERSION_ID_ARRAY_SIZE; i++)
{
if ((versionIDArray[i] != 0) &&
(versionIDArray[i] != 1) &&
(versionIDArray[i] != 2) &&
(versionIDArray[i] != 3))
return -1;
}
// ---------------------------------------------------------------
// Simply set newImage to this since there are no new version.
// ---------------------------------------------------------------
newImage = this;
}
else
{
// ---------------------------------------------------------------
// This is called from the subclass implementation of the same
// function. The subclass should have called reallocateImage() if
// imageSize_ was different from class size. Therefore, imageSize_
// should be the same as the most recent version class size now.
// ---------------------------------------------------------------
assert(newImage->imageSize_ == getClassSize());
}
return 0;
}
// ---------------------------------------------------------------------
// Driver for Packing
//
// Should return a 64 bit integer on a 64 bit platform. Could be fixed
// later when 64-bit platforms are really available since it doesn't
// affect object layout.
// ---------------------------------------------------------------------
Long NAVersionedObject::drivePack(void *space, short isSpacePtr)
{
// -----------------------------------------------------------------
// If the object has already been packed, just convert the pointer
// of the object to an offset and return its value. That value will
// be used by the caller to replace the pointer stored there.
// -----------------------------------------------------------------
if (isPacked())
{
if (isSpacePtr)
return ((Space *)space)->convertToOffset((char *)this);
else
return ((char *)space - (char *)this);
}
// -----------------------------------------------------------------
// Make sure the image size and the version ID are set properly
// before proceeding on to pack the object.
// -----------------------------------------------------------------
Int16 classSize = getClassSize();
if ((classSize % 8) != 0)
assert((classSize % 8) == 0);
setImageSize(classSize);
populateImageVersionIDArray();
// -----------------------------------------------------------------
// Toggle the Endianness of the Version Header if it's not stored
// in little-endian form.
//
// *** DON'T DO THIS JUST YET: PLAN IS TO SUPPORT THIS ONLY FROM
// *** SECOND RELEASE.
// -----------------------------------------------------------------
#ifndef NA_LITTLE_ENDIAN
// toggleEndiannessOfVersionHeader();
#endif
// -----------------------------------------------------------------
// Convert members of this object from local platform to reference
// platform.
//
// *** DON'T DO THIS JUST YET: PLAN IS TO SUPPORT THIS ONLY FROM
// *** SECOND RELEASE.
// -----------------------------------------------------------------
// convertToReferencePlatform();
// -----------------------------------------------------------------
// Mark object as packed, despite it is not completely packed yet.
// It is needed because the call that follows to the virtual method
// pack() drives the packing of all objects referenced by this
// object. If this object is subsequently referenced by another
// object down the row, drivePack() will be called on this object
// again. At that point of time, we should see the packed flag set
// so that "double-packing" can be avoided.
// -----------------------------------------------------------------
markAsPacked();
// -----------------------------------------------------------------
// pack() is a virtual method the subclass should redefine to drive
// the packing of all objects it references by pointers and convert
// those pointers to offsets. It should also convert the endianness
// of its members to the reference if necessary.
// -----------------------------------------------------------------
setIsSpacePtr( isSpacePtr !=0 );
Long offset = pack(space);
// long offset = (isSpacePtr ? pack(space) : pack(space,0));
// -----------------------------------------------------------------
// Make sure the eyeCatcher_ field of the object is proper. Also
// clean up the virtual table function pointer, so that the image
// look identical each time.
// -----------------------------------------------------------------
str_cpy_all(eyeCatcher_,VOBJ_EYE_CATCHER,VOBJ_EYE_CATCHER_SIZE);
setVTblPtr(NULL);
return offset;
}
// ---------------------------------------------------------------------
// Driver for Unpacking
//
// In a nutshell, unpacking consists of the following major steps:
//
// 1. fix the endianness of the version header (members of this class)
// 2. fix up the virtual table function pointer for the object
// 3. migrate an object of a previous version to the current version
// 4. fix the endianness of all other members in the subclasses
// 5. convert pointers in this object from offsets back to addresses
// 5. initiate unpacking for other objects referenced by this object
// 6. initialize new members added in the new version
//
// Parameters:
// base is the base address (added to offset to get pointer)
// vtblPtr (first form) is the pointer to the virtual function table
// of the class
// ptrToAnchorClass (second form, see below) is a pointer to an object
// that has the desired virtual function pointer
// ---------------------------------------------------------------------
NAVersionedObject *NAVersionedObject::driveUnpack(
void *base,
char *vtblPtr,
void * reallocator)
{
// -----------------------------------------------------------------
// Make sure we are really dealing with a NAVersionedObject by
// examining its eyeCatcher_.
// -----------------------------------------------------------------
if (str_cmp(eyeCatcher_,VOBJ_EYE_CATCHER,VOBJ_EYE_CATCHER_SIZE))
return NULL;
// -----------------------------------------------------------------
// If object has already been unpacked, just return either its own
// address or the reallocated address if the object was reallocated
// to somewhere else.
// -----------------------------------------------------------------
if (!isPacked())
{
if (reallocatedAddress_.isNull())
return this;
else
return reallocatedAddress_.getPointer();
}
else
reallocatedAddress_ = (NAVersionedObjectPtr) NULL ;
// -----------------------------------------------------------------
// Fix the Version Header to the endianness of the local platform
// if necessary. Correct classID_ field is necessary to determine
// the right subclass the object belongs.
//
// *** DON'T DO THIS JUST YET: PLAN IS TO SUPPORT THIS ONLY FROM
// *** SECOND RELEASE.
// -----------------------------------------------------------------
#ifndef NA_LITTLE_ENDIAN
// toggleEndiannessOfVersionHeader();
#endif
// -----------------------------------------------------------------
// The method findVTblPtr() was called on an object of the Anthor
// Class T to find out what subclass of the Anchor Class this object
// belongs based on its classID_. Now, the virtual function table
// pointer is used to fix up this object.
// -----------------------------------------------------------------
if (vtblPtr == NULL)
return NULL; // unknown class ID //
else
setVTblPtr(vtblPtr);
// -----------------------------------------------------------------
// Call the virtual method migrateToNewVersion() so that older
// objects can be migrated to fit in the new class template.
// -----------------------------------------------------------------
NAVersionedObject *objPtr = NULL;
// -----------------------------------------------------------------
// migrateToNewVersion() should either set objPtr to this or to an
// reallocated image.
// -----------------------------------------------------------------
if (migrateToNewVersion(objPtr))
return NULL; // version not supported //
// -----------------------------------------------------------------
// Convert members of this object from reference platform to local
// platform.
//
// *** DON'T DO THIS JUST YET: PLAN IS TO SUPPORT THIS ONLY FROM
// *** SECOND RELEASE.
// -----------------------------------------------------------------
// objPtr->convertToLocalPlatform();
// -----------------------------------------------------------------
// Mark object as not packed, despite it is not completely unpacked
// yet. This is needed because the call that follows to the virtual
// method unpack() drives the unpacking of all objects referenced by
// this object. If this object is subsequently referenced by another
// object down the row, driveUnpack() will be called on this object
// again. At that point of time, we should see the packed flag set
// to "not packed" so that "double-unpacking" can be avoided.
// -----------------------------------------------------------------
markAsNotPacked();
objPtr->markAsNotPacked();
// -----------------------------------------------------------------
// After migration, the object might have been reallocated. In that
// case objPtr will be changed to point to the reallocated object.
// The following calls are then made on the reallocated object
// instead of this.
// -----------------------------------------------------------------
if(objPtr->unpack(base, reallocator))
return NULL; // Should this ever happen? Internal Error.
objPtr->initNewMembers();
return objPtr;
}
NAVersionedObject *NAVersionedObject::driveUnpack(
void *base,
NAVersionedObject *ptrToAnchorClass,
void * reallocator)
{
// this method uses more stack space, due to the inline method
// findVTblPtr
char *vtblPtr = ptrToAnchorClass->findVTblPtr(classID_);
return driveUnpack(base,vtblPtr,reallocator);
}
// ------------------------------------------------------------ EOF ----