/* Copyright (C) 2003 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#ifndef NdbInterpretedCode_H
#define NdbInterpretedCode_H

#include <ndb_types.h>
#include <ndbapi_limits.h>

#include "NdbDictionary.hpp"
#include "NdbError.hpp"

class NdbTableImpl;
class NdbColumnImpl;

/*
  @brief Stand-alone interpreted programs, for use with NdbRecord

  @details This class is used to prepare an NDB interpreted program for use
  in operations created using NdbRecord, or scans created using the old
  API.  The ScanFilter class can also be used to generate an NDB interpreted
  program using NdbInterpretedCode.

  Usage :
    1) Create NdbInterpretedCode object, optionally supplying a table
       for the program to operate on, and a buffer for program storage
       and finalisation
       Note : 
       - If no table is supplied, then only instructions which do not
       access table attributes can be used.
       - If no buffer is supplied, then an internal buffer will be
       dynamically allocated and extended as necessary.
    2) Add instructions and labels to the NdbInterpretedCode object 
       by calling the methods below.
    3) When the program is complete, finalise it by calling the 
       finalise() method.  This will resolve internal branches and
       calls to label and subroutine offsets.
    4) Use the program with NdbRecord operations and scans by passing
       it at operation definition time via the OperationOptions or
       ScanOptions parameters.
       Alternatively, use the program with old-Api scans by passing it
       via the setInterpretedProgram() method.
    5) When the program is no longer required, the NdbInterpretedCode
       object can be deleted, along with any user-supplied buffer.

  Notes : 
    a) Each NDBAPI operation applies to one table, and so does any 
       NdbInterpretedCode program attached to that operation.
    b) A single finalised NdbInterpretedCode program can be used by
       more than one operation.  It need not be 'rebuilt' for each
       operation.
    c) Methods have minimal error checks, for efficiency
    d) Note that this interface may be subject to change without notice.
       The NdbScanFilter API is a more stable Api for defining scan-filter
       style programs.
*/
class NdbInterpretedCode
{
public:
  /**
   * NdbInterpretedCode constructor
   * 
   * @param table The table which this program will be run against.  This
   * parameter must be supplied if the program is table specific (i.e. 
   * reads from or writes to columns in the table).
   * @param buffer Pointer to a buffer of 32bit words used to store the 
   * program.  
   * @param buffer_word_size Length of the buffer passed in
   * If the program exceeds this length then adding new 
   * instructions will fail with error 4518, Too many instructions in 
   * interpreted program.
   *
   * Alternatively, if no buffer is passed, a buffer will be dynamically
   * allocated internally and extended to cope as instructions are
   * added.
   */
  NdbInterpretedCode(const NdbDictionary::Table *table= 0,
                     Uint32 *buffer= 0, 
                     Uint32 buffer_word_size= 0);

  ~NdbInterpretedCode();
  
  /* Register constant loads
   * -----------------------
   * These instructions allow numeric constants (and null)
   * to be loaded into the interpreter's registers
   * 
   * Space required      Buffer    Request message
   *   load_const_null   1 word    1 word
   *   load_const_u16    1 word    1 word
   *   load_const_u32    2 words   2 words
   *   load_const_u64    3 words   3 words
   *
   * @param RegDest Register to load constant into
   * @param Constant Value to load
   * @return 0 if successful, -1 otherwise
   */
  int load_const_null(Uint32 RegDest);
  int load_const_u16(Uint32 RegDest, Uint32 Constant);
  int load_const_u32(Uint32 RegDest, Uint32 Constant);
  int load_const_u64(Uint32 RegDest, Uint64 Constant);

  /* Register to / from table attribute load and store 
   * -------------------------------------------------
   * These instructions allow data to be moved between the
   * interpreter's numeric registers and numeric columns
   * in the current row.
   * These instructions require that the table being operated
   * on was specified with the NdbInterpretedCode object
   * was constructed.
   *
   * Space required   Buffer    Request message
   *   read_attr      1 word    1 word
   *   write_attr     1 word    1 word
   *
   * @param RegDest Register to load data into
   * @param attrId Table attribute to use
   * @param column Table column to use
   * @param RegSource Register to store data from
   * @return 0 if successful, -1 otherwise
   */
  int read_attr(Uint32 RegDest, Uint32 attrId);
  int read_attr(Uint32 RegDest, const NdbDictionary::Column *column);
  int write_attr(Uint32 attrId, Uint32 RegSource);
  int write_attr(const NdbDictionary::Column *column, Uint32 RegSource);

  /* Register arithmetic
   * -------------------
   * These instructions provide arithmetic operations on the
   * interpreter's registers.
   *
   * *RegDest= *RegSouce1 <operator> *RegSource2
   *
   * Space required   Buffer    Request message
   *   add_reg        1 word    1 word
   *   sub_reg        1 word    1 word
   *
   * @param RegDest Register to store operation result in
   * @param RegSource1 Register to use as LHS of operator
   * @param RegSource2 Register to use as RHS of operator
   * @return 0 if successful, -1 otherwise
   */
  int add_reg(Uint32 RegDest, Uint32 RegSource1, Uint32 RegSource2);
  int sub_reg(Uint32 RegDest, Uint32 RegSource1, Uint32 RegSource2);

  /* Control flow 
   * ------------
   */

  /* Label definition
   * ----------------
   * Space required   Buffer    Request message
   *   def_label      2 words   0 words
   *
   * @param LabelNum Unique number within this program for the label
   * @return 0 if successful, -1 otherwise
   */
  int def_label(int LabelNum);

  /* Unconditional jump 
   * ------------------
   * Space required   Buffer    Request message
   *   branch_label   1 word    1 word
   *
   * @param Label : Program label to jump to
   * @return 0 if successful, -1 otherwise
   */
  int branch_label(Uint32 Label);

  /* Register based conditional branch ops 
   * -------------------------------------
   * These instructions are used to branch based on numeric
   * register to register comparisons.
   *
   * if (RegLvalue <cond> RegRvalue)
   *   goto Label;
   *
   * Space required   Buffer    Request message
   *   branch_*       1 word    1 word
   *
   * @param RegLValue register to use as left hand side of condition
   * @param RegRValue register to use as right hand side of condition
   * @param Label Program label to jump to if condition is true
   * @return 0 if successfull, -1 otherwise.
   */
  int branch_ge(Uint32 RegLvalue, Uint32 RegRvalue, Uint32 Label);
  int branch_gt(Uint32 RegLvalue, Uint32 RegRvalue, Uint32 Label);
  int branch_le(Uint32 RegLvalue, Uint32 RegRvalue, Uint32 Label);
  int branch_lt(Uint32 RegLvalue, Uint32 RegRvalue, Uint32 Label);
  int branch_eq(Uint32 RegLvalue, Uint32 RegRvalue, Uint32 Label);
  int branch_ne(Uint32 RegLvalue, Uint32 RegRvalue, Uint32 Label);
  int branch_ne_null(Uint32 RegLvalue, Uint32 Label);
  int branch_eq_null(Uint32 RegLvalue, Uint32 Label);


  /* Table data based conditional branch ops
   * ---------------------------------------
   * These instructions are used to branch based on comparisons
   * between columns and constants.  
   *
   * These instructions require that the table being operated
   * upon was supplied when the NdbInterpretedCode object was
   * constructed.
   *
   * The comparison constant pointed to by val should
   * be in normal column format as described in the
   * documentation for NdbOperation.equal()
   * NOTE THE ORDER OF THE COMPARISON AND ARGUMENTS
   * 
   * if ( *val <cond> ValueOf(AttrId) )
   *   goto Label;
   *
   * Space required        Buffer          Request message
   *   branch_col_*_null   2 words         2 words
   *   branch_col_*        2 words +       2 words + 
   *                       len bytes       len bytes
   *                       rounded to      rounded to
   *                       nearest word    nearest word
   *
   * @param val       ptr to const value to compare against
   * @param len       length in bytes of const value
   * @param attrId    column to compare
   * @param Label     Program label to jump to if condition is true
   * @return 0 if successful, -1 otherwise.
   */
  int branch_col_eq(const void * val, Uint32 len, Uint32 attrId,
                    Uint32 Label);
  int branch_col_ne(const void * val, Uint32 len, Uint32 attrId,
                    Uint32 Label);
  int branch_col_lt(const void * val, Uint32 len, Uint32 attrId,
                    Uint32 Label);
  int branch_col_le(const void * val, Uint32 len, Uint32 attrId,
                    Uint32 Label);
  int branch_col_gt(const void * val, Uint32 len, Uint32 attrId,
                    Uint32 Label);
  int branch_col_ge(const void * val, Uint32 len, Uint32 attrId,
                    Uint32 Label);
  int branch_col_eq_null(Uint32 attrId, Uint32 Label);
  int branch_col_ne_null(Uint32 attrId, Uint32 Label);


  /* Table based pattern match conditional operations
   * ------------------------------------------------
   * These instructions are used to branch based on comparisons
   * between CHAR/BINARY/VARCHAR/VARBINARY columns and 
   * reg-exp patterns.
   *
   * These instructions require that the table being operated
   * upon was supplied when the NdbInterpretedCode object was
   * constructed.
   *
   * The pattern passed in val should be in plain CHAR
   * format even if the column is a VARCHAR
   * (i.e. no leading length bytes)
   *
   * if (ValueOf(attrId) <LIKE/NOTLIKE> *val)
   *   goto Label;
   *
   * Space required        Buffer          Request message
   *   branch_col_like/
   *   branch_col_notlike  2 words +       2 words + 
   *                       len bytes       len bytes
   *                       rounded to      rounded to
   *                       nearest word    nearest word
   *
   * @param val       ptr to const pattern to match against
   * @param len       length in bytes of const pattern
   * @param attrId    column to compare
   * @param Label     Program label to jump to if condition is true
   * @return 0 if successful, -1 otherwise.

   */
  int branch_col_like(const void * val, Uint32 len, Uint32 attrId,
                      Uint32 Label);
  int branch_col_notlike(const void * val, Uint32 len, Uint32 attrId,
                         Uint32 Label);

  /* Program results 
   * ---------------
   * These instructions indicate to the interpreter that processing
   * for the current row is finished.
   * In a scanning operation, the program may then be re-run for 
   * the next row.
   * In a non-scanning operation, the program will not be run again.
   * 
   */

  /* interpret_exit_ok
   * 
   * Scanning operation     : This row should be returned as part of
   *                          the scan.  Move onto next row.
   * Non-scanning operation : Exit interpreted program.
   *
   * Space required        Buffer    Request message
   *   interpret_exit_ok   1 word    1 word
   *
   * @return 0 if successful, -1 otherwise.
   */
  int interpret_exit_ok();
  
  /* interpret_exit_nok
   *
   * Scanning operation     : This row should not be returned as part
   *                          of the scan.  Move onto next row.
   * Non-scanning operation : Abort the operation
   *
   * Space required        Buffer    Request message
   *   interpret_exit_nok  1 word    1 word   
   *
   * @param ErrorCode An error code which will be returned as part
   * of the operation.  If not supplied, defaults to 899.
   * @return 0 if successful, -1 otherwise
   */
  int interpret_exit_nok(Uint32 ErrorCode);
  int interpret_exit_nok();

  /* interpret_exit_last_row
   * 
   * Scanning operation     : This row should be returned as part of
   *                          the scan.  No more rows should be scanned
   *                          in this fragment.
   * Non-scanning operation : Abort the operation
   *
   * Space required               Buffer    Request message
   *   interpret_exit_last_row    1 word    1 word
   *
   * @return 0 if successful, -1 otherwise
   */
  int interpret_exit_last_row();

  /* Utilities
   * These utilities insert multiple instructions into the
   * program and use specific registers to accomplish their 
   * goal.
   */

  /* add_val
   * Adds the supplied numeric value (32 or 64 bit) to the supplied
   * column.
   *
   * Uses registers 6 and 7, destroying any contents they have.
   * After execution : R6 = old column value  R7 = new column value
   *
   * These utilities require that the table being operated
   * upon was supplied when the NdbInterpretedCode object was
   * constructed.
   *
   * Space required     Buffer     Request message
   *   add_val(32bit)   4 words + 1 word if aValue >= 2^16
   *   add_val(64 bit)  4 words + 1 word if aValue >= 2^16
   *                            + 1 word if aValue >= 2^32
   *
   * @param attrId Column to be added to
   * @param aValue Value to add
   * @return 0 if successful, -1 otherwise
   */
  int add_val(Uint32 attrId, Uint32 aValue);
  int add_val(Uint32 attrId, Uint64 aValue);

  /* sub_val
   * Subtracts the supplied value (32 or 64 bit) from the
   * value of the supplied column.
   *
   * Uses registers 6 and 7, destroying any contents they have.
   * After execution : R6 = old column value  R7 = new column value
   *
   * These utilities require that the table being operated
   * upon was supplied when the NdbInterpretedCode object was
   * constructed.
   *
   * Space required     Buffer     Request message
   *   sub_val(32bit)   4 words + 1 word if aValue >= 2^16
   *   sub_val(64 bit)  4 words + 1 word if aValue >= 2^16
   *                            + 1 word if aValue >= 2^32
   *
   * @param attrId Column to be subtracted from
   * @param aValue Value to subtrace
   * @param 0 if successful, -1 otherwise
   */
  int sub_val(Uint32 attrId, Uint32 aValue);
  int sub_val(Uint32 attrId, Uint64 aValue);


  /* Subroutines
   * Subroutines which can be called from the 'main' part of
   * an interpreted program can be defined.
   * Subroutines are identified with a number.  Subroutine
   * numbers must be contiguous.
   */

  /**
   * def_subroutine
   * Define a subroutine.  Subroutines can only be defined 
   * after all main program instructions are defined.
   * Instructions following this, up to the next ret_sub() 
   * instruction are part of this subroutine.  
   * Subroutine numbers must be contiguous from zero but do 
   * not have to be in order.
   *
   * Space required     Buffer     Request message
   *   def_sub          2 words    0 words
   *
   * @param SubroutineNumber number to identify this subroutine
   * @return 0 if successful, -1 otherwise
   */
  int def_sub(Uint32 SubroutineNumber);

  /**
   * call_sub
   * Call a subroutine by number.  When the subroutine
   * returns, the program will continue executing at the
   * next instruction.  Subroutines can be called from the
   * main program, or from subroutines.
   * The maximum stack depth is currently 32.
   *
   * Space required     Buffer     Request message
   *   call_sub         1 word     1 word
   *
   * @param SubroutineNumber Which subroutine to call
   * @return 0 if successful, -1 otherwise
   */
  int call_sub(Uint32 SubroutineNumber);

  /**
   * ret_sub
   * Return from a subroutine.
   *
   * Space required     Buffer     Request message
   *   ret_sub          1 word     1 word
   *
   * @return 0 if successful, -1 otherwise
   */
  int ret_sub();

  /**
   * finalise
   * This method must be called after an Interpreted program 
   * is defined and before it is used.
   * It uses the label and subroutine meta information to 
   * resolve branch jumps and subroutine calls.
   * It can only be called once.
   * If no instructions have been defined, then it will attempt
   * to add a single interpret_exit_ok instruction before
   * finalisation.
   */
  int finalise();

  /**
   * getTable()
   * Returns a pointer to the table object representing the table
   * that this NdbInterpretedCode object operates on.
   * This can be NULL if no table object was supplied at 
   * construction time.
   */
  const NdbDictionary::Table* getTable() const;

  /**
   * getNdbError
   * This method returns the most recent error associated
   * with this NdbInterpretedCode object.
   */
  const class NdbError & getNdbError() const;


  /**
   * getWordsUsed
   * Returns the number of words of the supplied or internal
   * buffer that have been used.
   */
  Uint32 getWordsUsed() const;

private:
  friend class NdbOperation;
  friend class NdbScanOperation;

  static const Uint32 MaxReg= 8;
  static const Uint32 MaxLabels= 65535;
  static const Uint32 MaxSubs=65535;
  static const Uint32 MaxDynamicBufSize= NDB_MAX_SCANFILTER_SIZE_IN_WORDS;

  const NdbTableImpl *m_table_impl;
  Uint32 *m_buffer;
  Uint32 m_buffer_length;               // In words
  Uint32 *m_internal_buffer;            // Self-managed buffer
  Uint32 m_number_of_labels;
  Uint32 m_number_of_subs;
  Uint32 m_number_of_calls;
  
  /* Offset of last meta info record from start of m_buffer
   * in words
   */
  Uint32 m_last_meta_pos;

  /* Number of words used for instructions. Includes main program
   * and subroutines
   */
  Uint32 m_instructions_length;

  /* Position of first subroutine word.
   * 0 if there are no subroutines.
   */
  Uint32 m_first_sub_instruction_pos;

  /* The end of the buffer is used to store label and subroutine
   * meta information used when resolving branches and calls when
   * the program is finalised.
   * As this meta information grows, the remaining words in the
   * buffer may be less than buffer length minus the 
   * instructions length
   */
  Uint32 m_available_length;

  enum Flags {
    /*
      We will set m_got_error if an error occurs, so that we can
      refuse to create an operation from InterpretedCode that the user
      forgot to do error checks on.
    */
    GotError= 0x1,
    /* Set if reading disk column. */
    UsesDisk= 0x2,
    /* Object state : Set if currently defining a subroutine */
    InSubroutineDef= 0x4,
    /* Has this program been finalised? */
    Finalised= 0x8
  };
  Uint32 m_flags;

  NdbError m_error;

  enum InfoType {
    Label      = 0,
    Subroutine = 1};

  /* Instances of this type are stored at the end of
   * the buffer to describe label and subroutine
   * positions.  The instances are added as the labels and
   * subroutines are defined, so the order (working backwards
   * from the end of the buffer) would be : 
   *
   *   Main program labels (if any)
   *   First subroutine (if any)
   *   First subroutine label defs (if any)
   *   Second subroutine (if any)
   *   Second subroutine lable defs ....
   * 
   * The subroutines should be in order of subroutine number
   * as they must be defined in-order.  The labels can be in
   * any order.
   *
   * Before this information is used for finalisation, it is
   * sorted so that the subroutines and labels are in-order.
   */
  struct CodeMetaInfo
  { 
    Uint16 type;
    Uint16 number;         // Label or sub num
    Uint16 firstInstrPos;  // Offset from start of m_buffer, or
                           // from start of subs section for
                           // subs defs
  };
  
  static const Uint32 CODEMETAINFO_WORDS = 2;


  enum Errors 
  {
    TooManyInstructions = 4518,
    BadAttributeId      = 4004,
    BadLabelNum         = 4226,
    BranchToBadLabel    = 4221,
    BadLength           = 4209,
    BadSubNumber        = 4227,
    BadState            = 4231
  };

  int error(Uint32 code);
  bool have_space_for(Uint32 wordsRequired);

  int add1(Uint32 x1);
  int add2(Uint32 x1, Uint32 x2);
  int add3(Uint32 x1, Uint32 x2, Uint32 x3);
  int addN(Uint32 *data, Uint32 length);
  int addMeta(CodeMetaInfo& info);

  int add_branch(Uint32 instruction, Uint32 Label);
  int read_attr_impl(const NdbColumnImpl *c, Uint32 RegDest);
  int write_attr_impl(const NdbColumnImpl *c, Uint32 RegSource);
  int branch_col(Uint32 branch_type, Uint32 attrId, const void * val,
                 Uint32 len, Uint32 label);
  int getInfo(Uint32 number, CodeMetaInfo &info) const;
  static int compareMetaInfo(const void *a, 
                             const void *b);

};
#endif

