text: /**********************************************************************
  // @@@ 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 @@@
  **********************************************************************/
  #ifndef TRIGGERS_H
  #define TRIGGERS_H
  /* -*-C++-*-
  ********************************************************************************
  *
  * File:         Triggers.h
  * Description:	Definition of trigger objects and repository of common 
  *				definitions related to triggers.
  * Created:		06/23/98
  *
  *
  *
  *******************************************************************************
  */
  
  #include "NABasicObject.h"
  #include "Collections.h"
  #include "ItemConstr.h"
  #include "ComSmallDefs.h"
  #include "NAString.h"
  #include "ExecuteIdTrig.h"
  #include "charinfo.h"
  
  //-----------------------------------------------------------------------------
  // classes defined in this file:
  
  class Trigger;
  class MVImmediate;
  class UpdateColumns;
  class TriggerList;
  class BeforeAndAfterTriggers;
  
  //-----------------------------------------------------------------------------
  // Forward references
  class SqlTableOpenInfo;
  class StmtDDLCreateTrigger;
  class MvRefreshBuilder;
  
  //-----------------------------------------------------------------------------
  //
  // -- Transition Variables OLD/NEW Common Constant Variables
  //
  #undef  INIT_
  #undef  GLOB_
  #ifdef INITIALIZE_OLD_AND_NEW_NAMES
    #define INIT_(val)      = val
  #else
    #define INIT_(val)
  #endif
  
  // const Globals that use the FUNNY_INTERNAL_IDENT macro should be extern.
  
  // The OLD and NEW transition values.
  extern const char OLDCorr[] INIT_(FUNNY_INTERNAL_IDENT("OLD"));	//  OLD@
  extern const char NEWCorr[] INIT_(FUNNY_INTERNAL_IDENT("NEW"));	//  NEW@
  extern const char OLDAnsi[] INIT_(FUNNY_ANSI_IDENT("OLD"));	// "OLD@"
  extern const char NEWAnsi[] INIT_(FUNNY_ANSI_IDENT("NEW"));	// "NEW@"
  
  // -- Triggers-Temporary Table Common Constant Variables
  extern const char OLD_COLUMN_PREFIX[]       INIT_(FUNNY_INTERNAL_IDENT("OLD"));
  extern const char NEW_COLUMN_PREFIX[]       INIT_(FUNNY_INTERNAL_IDENT("NEW")); 
  
  // TBD: to be eliminated, both function and variable
  const char TRIG_TEMP_TABLE_SUFFIX[]="__TEMP"; 
  // The following two functions are used to convert the name of the subject
  // table to the name of its triggers-temporary table and vise versa.
  // (This is an "abstraction barrier": The rest of the code should only use
  //  these calls, and have no internal knowledge of implementation.  Our
  //  eventual goal is to have the two names be identical; this will be done
  //  solely by changing the implementation of the following functions.)
  NAString subjectNameToTrigTemp( const NAString & subjectTableName );      
  NAString trigTempToSubjectName( const NAString & trigTempTableName );
  
  const char UNIQUEEXE_COLUMN []=  "@UNIQUE_EXECUTE_ID";
  const char UNIQUEEXE_QCOLUMN[]="\"@UNIQUE_EXECUTE_ID\"";
  const char UNIQUEIUD_COLUMN []=  "@UNIQUE_IUD_ID";
  const char UNIQUEIUD_QCOLUMN[]="\"@UNIQUE_IUD_ID\"";
  
  //-----------------------------------------------------------------------------
  //
  // -- class Trigger
  
  class Trigger : public NABasicObject 
  {
  public:	
  
    // Explicit ctor
    Trigger(const QualifiedName    &name,
  		 const QualifiedName	&subjectTable,
  		 ComOperation		operation,
  		 ComActivationTime	activation,
  		 ComGranularity		granularity,
  		 ComTimestamp		timeStamp,
  		 NAString              *sqlText,
  		 CharInfo::CharSet      sqlTextCharSet,
  		 UpdateColumns	       *updateCols = NULL
          )
    :  name_(name),
       subjectTable_(subjectTable),
       operation_(operation),
       activation_(activation),
       granularity_(granularity),
       timeStamp_(timeStamp),
       sqlText_(sqlText),
       sqlTextCharSet_(sqlTextCharSet),
       updateCols_(updateCols),
       recursionCounter_(0),
       parsedTrigger_(NULL),
       isVirginCopy_(FALSE)
    {}
  
    // copy ctor
    // should not use
    Trigger (const Trigger &other)
     :  name_(other.name_),
        subjectTable_(other.subjectTable_),
        operation_(other.operation_),
        activation_(other.activation_),
        granularity_(other.granularity_),
        timeStamp_(timeStamp_),
        updateCols_(other.updateCols_)
        // Should never be called. Supplied only for collections.
    { CMPASSERT(FALSE); }
  
    // dtor
    virtual ~Trigger();
    
    // assignment operator
    // should not use
    Trigger&  operator = (const Trigger& other)
      // Should never be called. Supplied only because of collections.
      { CMPASSERT(FALSE); return *this; }
  
    // equality operator
    NABoolean operator ==(const Trigger &other) const;
  
    // Accessors & mutators
  
    const inline NAString	getTriggerName()		const 
      { return name_.getQualifiedNameAsAnsiString(); }
    const inline NAString	getSubjectTableName()		const 
      { return subjectTable_.getQualifiedNameAsAnsiString(); }
  
    // used for debugging only - print methods
    inline ComOperation getOperation()		        const 
      { return operation_; }
    inline NABoolean  isBeforeTrigger() const 
      { return (activation_ == COM_BEFORE); }
    inline NABoolean  isStatementTrigger() const 
      { return (granularity_== COM_STATEMENT); }
  
    inline NABoolean  isAfterTrigger() const 
      { return (activation_ == COM_AFTER); }
    inline NABoolean  isRowTrigger() const 
      { return (granularity_== COM_ROW); }
  
    // MV
    virtual NABoolean isMVImmediate() const
      { return false; } // this is a regular trigger
  
    const inline UpdateColumns *getUpdateColumns()	const 
      { return updateCols_; }
    const inline NAString * getSqlText()			const 
      { return sqlText_; } 
    inline CharInfo::CharSet getSqlTextCharSet()		const 
      { return sqlTextCharSet_; } 
    inline ComTimestamp getTimeStamp()		        const 
      { return timeStamp_; }
  
    // return a COPY of the parsed trigger (RelExpr tree) allocated from the
    // statementHeap. Recursion limit checking is also done here: an error-node
    // is returned and a compile-time warning is generated (if the recursion 
    // limit was exceeded)
    virtual RelExpr *getParsedTrigger(BindWA *bindWA);
    StmtDDLCreateTrigger *getCreateTriggerNode() const { return parsedTrigger_; }
  
    // recursion counter used to enforce the limit on the depth of recursive
    // triggers. Inc/Dec are called from RelExpr::bindChildren.
    inline Int32  getRecursionCounter()       const { return recursionCounter_; }
    inline void incRecursionCounter()       { recursionCounter_++; }
    inline void decRecursionCounter()       { recursionCounter_--; }
    
    // no longer used
    inline void resetRecursionCounter()		{ recursionCounter_=0; }
  
    //-------------------------------------------------------------------------
    //				Memory Management and Trigger Persistence
    // A trigger object is always created by the Catman (readTriggerDef). 
    // Currently, it's memory is managed as part of the StatementHeap. When
    // Triggers become persistent across statements, it's meomery will be 
    // managed as part of the ContextHeap, and then there are cases where the 
    // Trigger dtor is called and then it deallocates objects pointed to by the 
    // Trigger object as well.
    //
    // Please refer to the "Reusing Trigger Objects" Section of the detailed 
    // design document for more information on persistence across statements of 
    // trigger objects.
    //
    // the Heap() definition is originally intended to control the 
    // heap from which all data stored in the TriggerDB (including all
    // classes and members declared in this file and the TriggerDB itself) 
    // will be allocated. In order to make TriggerDB persistant across 
    // statements, the definition should have been CmpCommon::contextHeap(). 
    // However, in this version, some RelExpr and ItemExpr nodes cannot be 
    // allocated or copied to/from the ContextHeap, since some ctor and 
    // copying code specifies CmpCommon::statementHeap() explicitly, regardless 
    // of the parameter given to the overloaded new operator.
    // Therefore, untill this is fixed, TriggerDB and its content cannot be
    // allocated from the CmpCommon::contextHeap(), and is not persistent 
    // across statements. 
    //
    // The Heap() method is the ONLY thing that needs to be changed to activate
    // Trigger persistence.
    //-------------------------------------------------------------------------
    inline static NAMemory* Heap()
      { return CmpCommon::statementHeap(); }
  
    // for debug
    void print(ostream& os, const char* indent, const char* title) const;
  
  private:
  
    StmtDDLCreateTrigger *parseTriggerText() const;
  
    const QualifiedName   name_;
    const QualifiedName   subjectTable_;
    const UpdateColumns  *updateCols_;
  
    const ComOperation	operation_;		// Insert/Update/Delete (IUD)
    const ComActivationTime activation_;		// Before/After
    const ComGranularity	granularity_;		// Row/Statement
    const ComTimestamp	timeStamp_;		// creation time
  
    NAString	       *sqlText_;
    CharInfo::CharSet     sqlTextCharSet_;
    StmtDDLCreateTrigger *parsedTrigger_;	
    short			recursionCounter_;
  
    // Is this the result of the first parse, or a copy of the second?
    NABoolean		isVirginCopy_;	    
  
  };
  
  //-----------------------------------------------------------------------------
  //
  // -- class MVImmediate
  //
  // MVImmediate implements the refreshing of ON STATEMENT MVs using the Triggers
  // backbone. Each such MV is considered as a special type of trigger. This
  // trigger is an after-trigger with special timestamp to ensure its firing before
  // any other trigger defined on the same subject table. The sqlText parameter is
  // not in use. This trigger differs from a regular trigger only in the behaviour
  // of the virtual method getParsedTrigger. Unlike regular triggers, this one is
  // not originated from a "create trigger" statement. The appropriate refresh
  // tree is determined by the builder (MvRefreshBuilder object) given to it by
  // parameter.
  //
  // Important note on timestamps:
  // ------------------------------
  // Since indirect-update operations consists two such triggers, we must ensure
  // these triggers have unique timestamps (so the sorting according timestamps
  // will be deterministic). The "row trigger" part will have timestamp of zero,
  // while the "statement trigger" part will have timestamp of one. Since
  // timestamp of one is enough to be sure it will fire before any regular
  // trigger, we use it for any statement trigger of type MVImmediate.
  
  class MVImmediate : public Trigger {
  public:
  
    MVImmediate(BindWA              *bindWA,
  	      MvRefreshBuilder    *triggerBuilder,
  	      const QualifiedName &mvName,
  	      const QualifiedName &subjectTable,
  	      ComOperation	  operation,
  	      ComGranularity	  granularity,
  	      UpdateColumns	  *updateCols)  // should set to NULL if not used
    : Trigger(mvName,
  	    subjectTable,
  	    operation,
  	    COM_AFTER,
  	    granularity,
  	    (granularity == COM_STATEMENT ? 1L : 0L),
  	    new NAString(""), CharInfo::UnknownCharSet,
  	    updateCols),
      bindWA_(bindWA),
      triggerBuilder_(triggerBuilder)
    {};
  
    virtual RelExpr *getParsedTrigger(BindWA *bindWA);
    virtual NABoolean isMVImmediate() const 
      { return true; } // this is a special trigger (ON STATEMENT MV)
  
  private:
  
    BindWA *bindWA_; // for use by the builders
    MvRefreshBuilder *triggerBuilder_; // the builder to build the refresh tree
  };
  
  //-----------------------------------------------------------------------------
  //
  // -- class UpdateColumns
  //
  // Used to implement the match-any semantics of Update triggers: If there is 
  // an intersection between the set of subject columns and the set of updated 
  // columns, then the trigger is fired. An internal small hash table of column 
  // positions is used to implement the intersection. If there are no explicit 
  // subject columns, then all columns are considered as subject column and the 
  // case is represented especially by isAllColumns_.
  // Notice that Trigger::Heap() is used for memory allocation.
  
  static const short INIT_SUBJECT_COLUMNS_NUMBER = 10;
   
  class UpdateColumns : public NABasicObject 
  {
  public:
  
    UpdateColumns(NABoolean allColumns)
      :  allColumns_(allColumns)
    {
      if (!allColumns)
        columns_ = new (Trigger::Heap()) SET(Lng32) (Trigger::Heap());
      else
        columns_ = NULL;
    }
  
    // Ctor from STOI column list
    UpdateColumns(SqlTableOpenInfo *stoi);
  
    virtual ~UpdateColumns();
    
    inline void addColumn(const Lng32 column) { columns_->insert(column); }  
  
    // Implements match-any: It is enough that there is a common column in the 
    // subject columns and in the updated columns for the relevant trigger to 
    // fire.
    NABoolean match(const UpdateColumns &other) const;  
  
    inline NABoolean contains(const Lng32 col) const
  	  { return columns_->contains(col); }
  
    inline NABoolean isAllColumns()	      const { return allColumns_; }
  
    void markColumnsOnBitmap(unsigned char *bitmap, CollIndex numBytes) const;
  
    void print(ostream& os, const char* indent, const char* title) const;
    
  private:
  
    NABoolean		 allColumns_;	// no explicit subject columns
    SET(Lng32)*             columns_;
    
  }; 
  
  
  //-----------------------------------------------------------------------------
  //
  // -- class TriggerList
  
  class TriggerList : public LIST(Trigger *) 
  {
  public:
  
    TriggerList(NAMemory * h): LIST(Trigger *)(h) {};
  
    // returns the triggers whose subject columns intersect the given updateCols
    TriggerList* getColumnMatchingTriggers(const UpdateColumns *updateCols);
  
    // internally sorts the list by time stamp, so that (*this)[0] is the oldest Trigger
    void sortByTimeStamp();
  
    // free the memory of all the Trigger objects of the list
    void clearAndDestroy();
  
    void print(ostream& os, const char* indent, const char* title) const;
  
  };
  
  //-----------------------------------------------------------------------------
  //
  //
  // -- class BeforeAndAfterTriggers
  //
  // Used only to group together existing triggers according to their activation
  // time. Serves as the Value of the triggerDB hash.
  //
  class BeforeAndAfterTriggers : public NABasicObject
  {
  public:
  
    // explicit ctor, with timestamp, just copies pointers
    BeforeAndAfterTriggers(TriggerList *beforeTriggers, 
  			 TriggerList *afterStatementTriggers,
  			 TriggerList *afterRowTriggers,
  			 ComTimestamp subjectTableTimestamp,
                           ComPrivilegeChecks doPrivCheck = COM_PRIV_NO_CHECK) 
    : beforeTriggers_(beforeTriggers),
      afterStatementTriggers_(afterStatementTriggers),
      afterRowTriggers_(afterRowTriggers),
      subjectTableTimestamp_(subjectTableTimestamp),
      doPrivCheck_(doPrivCheck)
    {}
  
    virtual ~BeforeAndAfterTriggers();
  
    // free the memory of all the lists referenced by this object
    void clearAndDestroy();
  
    // Accessors
    inline TriggerList *getBeforeTriggers()	  const 
      { return beforeTriggers_; }
    inline TriggerList *getAfterStatementTriggers() const 
      { return afterStatementTriggers_;  }
    inline TriggerList *getAfterRowTriggers()	  const 
      { return afterRowTriggers_;  }
    inline ComTimestamp getSubjectTableTimeStamp()  const 
      { return subjectTableTimestamp_; }
    
    Lng32 entries() const;
  
    // initialize lists for insertions
    void addNewAfterStatementTrigger(Trigger *newTrigger);
    void addNewAfterRowTrigger(Trigger *newTrigger);
  
    // Should never be called. Needed for compilation. We rely on 
    // NAHashBucket::contains() being called with a default NULL argument for
    // Values in the triggerDB hash where BeforeAndAfterTriggers are Values.
    NABoolean operator == (const BeforeAndAfterTriggers &other) const 
      { CMPASSERT(FALSE); return FALSE; }
  
    void print(ostream& os, const char* indent, const char* title) const;
  
  private:
    TriggerList *beforeTriggers_;
    TriggerList *afterStatementTriggers_;
    TriggerList *afterRowTriggers_;
    ComTimestamp subjectTableTimestamp_; // used for validation purposes
    ComPrivilegeChecks doPrivCheck_;
  };
  
  #endif /* TRIGGERS_H */
  