| /********************************************************************** |
| // @@@ 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: AccessSets.cpp |
| * Description: Definition of class InliningInfo. |
| * |
| * Created: 6/23/98 |
| * Language: C++ |
| * Status: $State: Exp $ |
| * |
| * |
| ****************************************************************************** |
| */ |
| |
| #include "Sqlcomp.h" |
| #include "AllItemExpr.h" |
| #include "AllRelExpr.h" |
| #include "GroupAttr.h" |
| #include "AccessSets.h" |
| |
| static const Int32 INITIAL_SIZE_OF_READ_SET = 10; |
| static const Int32 INITIAL_SIZE_OF_WRITE_SET = 5; |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Methods of class TableAccess |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| // Default ctor - to EMPTY. |
| TableAccess::TableAccess() |
| : tableID_(NULL), |
| empty_(TRUE) |
| { |
| } |
| |
| // Explicit ctor |
| TableAccess::TableAccess(const NATable *theTable) : |
| tableID_(const_cast<NATable *>(theTable)), |
| empty_(FALSE) |
| { |
| } |
| |
| // Copy ctor |
| TableAccess::TableAccess(const TableAccess &other) |
| : tableID_ (other.tableID_), |
| empty_ (other.empty_) |
| { |
| } |
| |
| // Assignment operator |
| TableAccess& TableAccess::operator= (const TableAccess& other) |
| { |
| tableID_ = other.tableID_; |
| empty_ = other.empty_; |
| return *this; |
| } |
| |
| NABoolean TableAccess::match(const TableAccess& other) const |
| { |
| return ( (tableID_ == other.tableID_ ) && |
| (empty_ == other.empty_ ) ); |
| } |
| |
| void TableAccess::print(FILE *ofd, const char *indent, const char *title) const |
| { |
| if (isEmpty()) |
| fprintf(ofd,"%s - Empty.\n", title); |
| else if (tableID_ == NULL) |
| fprintf(ofd,"%s - Opaque.\n", title); |
| else |
| { |
| BUMP_INDENT(indent); |
| fprintf(ofd,"%s%s this=%p, NATable=%p, ", |
| NEW_INDENT, title, this, tableID_); |
| fprintf(ofd,"%s\n", |
| tableID_->getTableName().getQualifiedNameAsString().data()); |
| } |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Methods of class ReadTableAccess |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| // Assignment operator |
| ReadTableAccess& ReadTableAccess::operator= (const ReadTableAccess& other) |
| { |
| tableID_ = other.tableID_; |
| empty_ = other.empty_; |
| // // Call the same operator on the parent class. |
| // (*(TableAccess *)this) = other; |
| return *this; |
| } |
| |
| // Comparison operator |
| NABoolean ReadTableAccess::operator ==(const TableAccess& other) const |
| { |
| return (match(other) && other.isReadAccess()); |
| } |
| |
| // A ReadTableAccess object only conflicts with another WriteTableAccess |
| // object, on the same table. |
| NABoolean ReadTableAccess::isConflicting(const TableAccess& other) const |
| { |
| CMPASSERT(!isEmpty() && !other.isEmpty()); |
| if (!isOpaque() && !other.isOpaque()) |
| if (!match(other)) |
| return FALSE; |
| |
| // The tables match. |
| return (!other.isReadAccess()); |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Methods of class WriteTableAccess |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| // Assignment operator |
| WriteTableAccess& WriteTableAccess::operator= (const WriteTableAccess& other) |
| { |
| tableID_ = other.tableID_; |
| empty_ = other.empty_; |
| // // Call the same operator on the parent class. |
| // (*(TableAccess *)this) = other; |
| return *this; |
| } |
| |
| // Comparison operator |
| NABoolean WriteTableAccess::operator ==(const TableAccess& other) const |
| { |
| return (match(other) && !other.isReadAccess()); |
| } |
| |
| // A WriteTableAccess object conflicts with all other TableAccess objects, |
| // on the same table. |
| NABoolean WriteTableAccess::isConflicting(const TableAccess& other) const |
| { |
| CMPASSERT(!isEmpty() && !other.isEmpty()); |
| if (!isOpaque() && !other.isOpaque()) |
| if (!match(other)) |
| return FALSE; |
| |
| // The tables match. |
| return TRUE; |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Methods of class SubTreeAccessSet |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| // Ctor |
| SubTreeAccessSet::SubTreeAccessSet(CollHeap *heap) |
| : opaqueForRead_(FALSE), |
| opaqueForWrite_(FALSE) |
| { |
| readSet_ = new(heap) SET(ReadTableAccessPtr )(heap,INITIAL_SIZE_OF_READ_SET); |
| writeSet_= new(heap) SET(WriteTableAccessPtr)(heap,INITIAL_SIZE_OF_WRITE_SET); |
| } |
| |
| // dtor |
| SubTreeAccessSet::~SubTreeAccessSet() |
| { |
| delete readSet_; |
| delete writeSet_; |
| } |
| |
| // Add a single ReadTableAccess object to the read set. |
| void SubTreeAccessSet::add(ReadTableAccessPtr snas) |
| { |
| // Since readSet_ is a set of pointers, it may contain duplicates. |
| // So before inserting - check if it's already in there. |
| for (CollIndex i=0; i<readSet_->entries(); i++) |
| if (*(readSet_->at(i)) == *snas) |
| return; |
| |
| CMPASSERT(!snas->isEmpty()); |
| |
| if (snas->isOpaque()) |
| opaqueForRead_ = TRUE; |
| readSet_->insert(snas); |
| } |
| |
| // Add a single WriteTableAccess object to the write set. |
| void SubTreeAccessSet::add(WriteTableAccessPtr snas) |
| { |
| // Since writeSet_ is a set of pointers, it may contain duplicates. |
| // So before inserting - check if it's already in there. |
| for (CollIndex i=0; i<writeSet_->entries(); i++) |
| if (*(writeSet_->at(i)) == *snas) |
| return; |
| |
| CMPASSERT(!snas->isEmpty()); |
| |
| if (snas->isOpaque()) |
| opaqueForWrite_ = TRUE; |
| writeSet_->insert(snas); |
| } |
| |
| // Merge another SubTreeAccessSet into this object. |
| void SubTreeAccessSet::add(const SubTreeAccessSet *stas) |
| { |
| if (stas == NULL) |
| return; |
| |
| CollIndex i=0; |
| for (i=0; i<stas->readSet_->entries(); i++) |
| add((*stas->readSet_)[i]); |
| |
| for (i=0; i<stas->writeSet_->entries(); i++) |
| add((*stas->writeSet_)[i]); |
| } |
| |
| // Does this access set conflict with a single TableAccess? |
| // Check if it conflicts with any of the contained objects. |
| NABoolean SubTreeAccessSet::isConflicting(const TableAccess *snas) const |
| { |
| CMPASSERT(!snas->isEmpty()); |
| |
| // First check if is conflicting with the write set |
| if (opaqueForWrite_) |
| return TRUE; |
| |
| CollIndex i=0; |
| for (i=0; i<writeSet_->entries(); i++) |
| if((*writeSet_)[i]->isConflicting(*snas)) |
| return TRUE; |
| |
| // For write operations, check the read set too. |
| if (!snas->isReadAccess()) |
| { |
| if (opaqueForRead_) |
| return TRUE; |
| |
| for (i=0; i<readSet_->entries(); i++) |
| if((*readSet_)[i]->isConflicting(*snas)) |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| // Does this access set conflict with a another SubTreeAccessSet? |
| // Check it against each and every contained object. |
| NABoolean SubTreeAccessSet::isConflicting(const SubTreeAccessSet *other) const |
| { |
| if (other == NULL) |
| return FALSE; |
| |
| // First check if this set is OPAQUE. |
| if (opaqueForWrite_ && !other->isEmpty()) |
| return TRUE; |
| |
| // Check against each and every table access in the write set. |
| CollIndex i=0; |
| for (i=0; i<writeSet_->entries(); i++) |
| if(other->isConflicting((*writeSet_)[i])) |
| return TRUE; |
| |
| // Check against each and every table access in the read set. |
| for (i=0; i<readSet_->entries(); i++) |
| if(other->isConflicting((*readSet_)[i])) |
| return TRUE; |
| |
| return FALSE; |
| } |
| |
| NABoolean SubTreeAccessSet::isEmpty() const |
| { |
| return (readSet_->isEmpty() && writeSet_->isEmpty()); |
| } |
| |
| void SubTreeAccessSet::print(FILE *ofd, const char *indent, const char *title) const |
| { |
| CollIndex i; |
| |
| BUMP_INDENT(indent); |
| fprintf(ofd,"%s%s %p \n", NEW_INDENT, title, this); |
| |
| if (this == NULL) |
| fprintf(ofd, "NULL\n"); |
| else |
| { |
| for (i = 0; i < readSet_->entries(); i++) |
| (*readSet_)[i]->print(ofd, indent); |
| if (i) |
| fprintf(ofd,"\n"); |
| |
| for (i = 0; i < writeSet_->entries(); i++) |
| (*writeSet_)[i]->print(ofd, indent); |
| if (i) |
| fprintf(ofd,"\n"); |
| } |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Methods of other classes using access sets. |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| // Collect SubTreeAccessSet information from the sub-tree below, and return |
| // it to the parent. |
| // When the isAccessSetNeeded flag is set, it means that the children's |
| // access sets should also be saved in this node. |
| SubTreeAccessSet * RelExpr::calcAccessSets(CollHeap *heap) |
| { |
| if (getInliningInfo().isAccessSetNeeded()) |
| { |
| // This is the Trigger backbone - save the children's access sets |
| // as data members of the RelExpr class. |
| // If no access set - put an empty set, in order to simplify |
| // the checkswhen checking for conflicts. |
| if (child(0) != NULL) |
| { |
| setAccessSet0(child(0)->calcAccessSets(heap)); |
| if (getAccessSet0() == NULL) |
| setAccessSet0(new(heap) SubTreeAccessSet(heap)); |
| } |
| |
| if (child(1) != NULL) |
| { |
| setAccessSet1(child(1)->calcAccessSets(heap)); |
| if (getAccessSet1() == NULL) |
| setAccessSet1(new(heap) SubTreeAccessSet(heap)); |
| } |
| |
| #ifndef NDEBUG |
| if (getenv("DEBUG_ACCESS_SETS")) |
| { |
| ExprNode::print(); |
| getAccessSet0()->print(); |
| getAccessSet1()->print(); |
| } |
| #endif |
| |
| // Create a fresh new object, add to it the children's access sets |
| // and return it up to the parent node. |
| SubTreeAccessSet *result = new(heap) SubTreeAccessSet(heap); |
| result->add(getAccessSet0()); |
| result->add(getAccessSet1()); |
| return(result); |
| } |
| else |
| { |
| // All the children's access sets are merged into one |
| // (the first one returned). |
| SubTreeAccessSet *stas = NULL; |
| |
| for (Int32 i=0; i<getArity(); i++) |
| if (child(i)) |
| if (stas==NULL) |
| stas = child(i)->calcAccessSets(heap); |
| else |
| stas->add(child(i)->calcAccessSets(heap)); |
| return (stas); |
| } |
| } |
| |
| // Create a new SubTreeAccessSet object and add to it the children's access |
| // sets and this node's read access. |
| SubTreeAccessSet * Scan::calcAccessSets(CollHeap *heap) |
| { |
| CMPASSERT(!getInliningInfo().isAccessSetNeeded()); |
| |
| // Ignore index tables, resource forks and other special tables. |
| switch (getTableDesc()->getNATable()->getSpecialType()) |
| { |
| case ExtendedQualName::NORMAL_TABLE : |
| case ExtendedQualName::TRIGTEMP_TABLE : |
| case ExtendedQualName::MV_TABLE : |
| case ExtendedQualName::EXCEPTION_TABLE: |
| case ExtendedQualName::GHOST_TABLE : |
| case ExtendedQualName::GHOST_MV_TABLE : |
| break; |
| default: |
| return RelExpr::calcAccessSets(heap); |
| } |
| |
| SubTreeAccessSet *result = RelExpr::calcAccessSets(heap); |
| if (result == NULL) |
| result = new(heap) SubTreeAccessSet(heap); |
| |
| const ReadTableAccessPtr readAccess = new(heap) |
| ReadTableAccess(getTableDesc()->getNATable()); |
| result->add(readAccess); |
| |
| return(result); |
| } |
| |
| // Create a new SubTreeAccessSet object and add to it the children's access |
| // sets and this node's write access. |
| SubTreeAccessSet * GenericUpdate::calcAccessSets(CollHeap *heap) |
| { |
| // Ignore index tables, resource forks and other special tables. |
| switch (getTableDesc()->getNATable()->getSpecialType()) |
| { |
| case ExtendedQualName::NORMAL_TABLE : |
| case ExtendedQualName::TRIGTEMP_TABLE : |
| case ExtendedQualName::MV_TABLE : |
| case ExtendedQualName::EXCEPTION_TABLE: |
| case ExtendedQualName::GHOST_TABLE : |
| case ExtendedQualName::GHOST_MV_TABLE : |
| break; |
| default: |
| return RelExpr::calcAccessSets(heap); |
| } |
| |
| SubTreeAccessSet *result = RelExpr::calcAccessSets(heap); |
| if (result == NULL) |
| result = new(heap) SubTreeAccessSet(heap); |
| |
| const WriteTableAccessPtr writeAccess = new(heap) |
| WriteTableAccess(getTableDesc()->getNATable()); |
| result->add(writeAccess); |
| |
| return(result); |
| } |