blob: f14beeb433823eb90def89caca8cb8a728bccd85 [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: 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);
}