| /********************************************************************** |
| // @@@ 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 ---- |
| |