int Dbtup::interpreterNextLab()

in storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp [3344:4127]


int Dbtup::interpreterNextLab(Signal *signal, KeyReqStruct *req_struct,
                              Uint32 *logMemory, Uint32 *mainProgram,
                              Uint32 TmainProgLen, Uint32 *subroutineProg,
                              Uint32 TsubroutineLen, Uint32 *tmpArea,
                              Uint32 tmpAreaSz) {
  Uint32 theRegister;
  Uint32 theInstruction;
  Uint32 TprogramCounter = 0;
  Uint32 *TcurrentProgram = mainProgram;
  Uint32 TcurrentSize = TmainProgLen;
  Uint32 TdataWritten = 0;
  Uint32 RstackPtr = 0;
  union {
    Uint32 TregMemBuffer[32];
    Uint64 align[16];
  };
  (void)align;  // kill warning
  Uint32 TstackMemBuffer[32];

  Uint32 &RnoOfInstructions = req_struct->no_exec_instructions;
  ndbassert(RnoOfInstructions == 0);
  /* ---------------------------------------------------------------- */
  // Initialise all 8 registers to contain the NULL value.
  // In this version we can handle 32 and 64 bit unsigned integers.
  // They are handled as 64 bit values. Thus the 32 most significant
  // bits are zeroed for 32 bit values.
  /* ---------------------------------------------------------------- */
  TregMemBuffer[0] = 0;
  TregMemBuffer[4] = 0;
  TregMemBuffer[8] = 0;
  TregMemBuffer[12] = 0;
  TregMemBuffer[16] = 0;
  TregMemBuffer[20] = 0;
  TregMemBuffer[24] = 0;
  TregMemBuffer[28] = 0;
  Uint32 tmpHabitant = ~0;

  while (RnoOfInstructions < 8000) {
    /* ---------------------------------------------------------------- */
    /* EXECUTE THE NEXT INTERPRETER INSTRUCTION.                        */
    /* ---------------------------------------------------------------- */
    RnoOfInstructions++;
    theInstruction = TcurrentProgram[TprogramCounter];
    theRegister = Interpreter::getReg1(theInstruction) << 2;
#ifdef TRACE_INTERPRETER
    g_eventLogger->info(
        "Interpreter :"
        " RnoOfInstructions : %u.  TprogramCounter : %u.  Opcode : %u",
        RnoOfInstructions, TprogramCounter,
        Interpreter::getOpCode(theInstruction));
#endif
    if (TprogramCounter < TcurrentSize) {
      TprogramCounter++;
      switch (Interpreter::getOpCode(theInstruction)) {
        case Interpreter::READ_ATTR_INTO_REG:
          jamDebug();
          /* ---------------------------------------------------------------- */
          // Read an attribute from the tuple into a register.
          // While reading an attribute we allow the attribute to be an array
          // as long as it fits in the 64 bits of the register.
          /* ---------------------------------------------------------------- */
          {
            Uint32 theAttrinfo = theInstruction;
            int TnoDataRW =
                readAttributes(req_struct, &theAttrinfo, (Uint32)1,
                               &TregMemBuffer[theRegister], (Uint32)3);
            if (TnoDataRW == 2) {
              /* -------------------------------------------------------------
               */
              // Two words read means that we get the instruction plus one 32
              // word read. Thus we set the register to be a 32 bit register.
              /* -------------------------------------------------------------
               */
              TregMemBuffer[theRegister] = 0x50;
              // arithmetic conversion if big-endian
              *(Int64 *)(TregMemBuffer + theRegister + 2) =
                  TregMemBuffer[theRegister + 1];
            } else if (TnoDataRW == 3) {
              /* -------------------------------------------------------------
               */
              // Three words read means that we get the instruction plus two
              // 32 words read. Thus we set the register to be a 64 bit
              // register.
              /* -------------------------------------------------------------
               */
              TregMemBuffer[theRegister] = 0x60;
              TregMemBuffer[theRegister + 3] = TregMemBuffer[theRegister + 2];
              TregMemBuffer[theRegister + 2] = TregMemBuffer[theRegister + 1];
            } else if (TnoDataRW == 1) {
              /* -------------------------------------------------------------
               */
              // One word read means that we must have read a NULL value. We set
              // the register to indicate a NULL value.
              /* -------------------------------------------------------------
               */
              TregMemBuffer[theRegister] = 0;
              TregMemBuffer[theRegister + 2] = 0;
              TregMemBuffer[theRegister + 3] = 0;
            } else if (TnoDataRW < 0) {
              jamDebug();
              terrorCode = Uint32(-TnoDataRW);
              tupkeyErrorLab(req_struct);
              return -1;
            } else {
              /* -------------------------------------------------------------
               */
              // Any other return value from the read attribute here is not
              // allowed and will lead to a system crash.
              /* -------------------------------------------------------------
               */
              ndbabort();
            }
            break;
          }

        case Interpreter::WRITE_ATTR_FROM_REG: {
          jamDebug();
          Uint32 TattrId = theInstruction >> 16;
          Uint32 TattrDescrIndex =
              req_struct->tablePtrP->tabDescriptor + (TattrId << ZAD_LOG_SIZE);
          Uint32 TattrDesc1 = tableDescriptor[TattrDescrIndex].tabDescr;
          Uint32 TregType = TregMemBuffer[theRegister];

          /* --------------------------------------------------------------- */
          // Calculate the number of words of this attribute.
          // We allow writes into arrays as long as they fit into the 64 bit
          // register size.
          /* --------------------------------------------------------------- */
          Uint32 TattrNoOfWords =
              AttributeDescriptor::getSizeInWords(TattrDesc1);
          Uint32 Toptype = req_struct->operPtrP->op_type;
          Uint32 TdataForUpdate[3];
          Uint32 Tlen;

          AttributeHeader ah(TattrId, TattrNoOfWords << 2);
          TdataForUpdate[0] = ah.m_value;
          TdataForUpdate[1] = TregMemBuffer[theRegister + 2];
          TdataForUpdate[2] = TregMemBuffer[theRegister + 3];
          Tlen = TattrNoOfWords + 1;
          if (Toptype == ZUPDATE) {
            if (TattrNoOfWords <= 2) {
              if (TattrNoOfWords == 1) {
                // arithmetic conversion if big-endian
                Int64 *tmp = new (&TregMemBuffer[theRegister + 2]) Int64;
                TdataForUpdate[1] = Uint32(*tmp);
                TdataForUpdate[2] = 0;
              }
              if (TregType == 0) {
                /* --------------------------------------------------------- */
                // Write a NULL value into the attribute
                /* --------------------------------------------------------- */
                ah.setNULL();
                TdataForUpdate[0] = ah.m_value;
                Tlen = 1;
              }
              int TnoDataRW =
                  updateAttributes(req_struct, &TdataForUpdate[0], Tlen);
              if (TnoDataRW >= 0) {
                /* --------------------------------------------------------- */
                // Write the written data also into the log buffer so that it
                // will be logged.
                /* --------------------------------------------------------- */
                logMemory[TdataWritten + 0] = TdataForUpdate[0];
                logMemory[TdataWritten + 1] = TdataForUpdate[1];
                logMemory[TdataWritten + 2] = TdataForUpdate[2];
                TdataWritten += Tlen;
              } else {
                terrorCode = Uint32(-TnoDataRW);
                tupkeyErrorLab(req_struct);
                return -1;
              }
            } else {
              return TUPKEY_abort(req_struct, 15);
            }
          } else {
            return TUPKEY_abort(req_struct, 16);
          }
          break;
        }

        case Interpreter::LOAD_CONST_NULL:
          jamDebug();
          TregMemBuffer[theRegister] = 0; /* NULL INDICATOR */
          break;

        case Interpreter::LOAD_CONST16:
          jamDebug();
          TregMemBuffer[theRegister] = 0x50; /* 32 BIT UNSIGNED CONSTANT */
          *(Int64 *)(TregMemBuffer + theRegister + 2) = theInstruction >> 16;
          break;

        case Interpreter::LOAD_CONST32:
          jamDebug();
          TregMemBuffer[theRegister] = 0x50; /* 32 BIT UNSIGNED CONSTANT */
          *(Int64 *)(TregMemBuffer + theRegister + 2) =
              *(TcurrentProgram + TprogramCounter);
          TprogramCounter++;
          break;

        case Interpreter::LOAD_CONST64:
          jamDebug();
          TregMemBuffer[theRegister] = 0x60; /* 64 BIT UNSIGNED CONSTANT */
          TregMemBuffer[theRegister + 2] =
              *(TcurrentProgram + TprogramCounter++);
          TregMemBuffer[theRegister + 3] =
              *(TcurrentProgram + TprogramCounter++);
          break;

        case Interpreter::ADD_REG_REG:
          jamDebug();
          {
            Uint32 TrightRegister = Interpreter::getReg2(theInstruction) << 2;
            Uint32 TdestRegister = Interpreter::getReg3(theInstruction) << 2;

            Uint32 TrightType = TregMemBuffer[TrightRegister];
            Int64 Tright0 = *(Int64 *)(TregMemBuffer + TrightRegister + 2);

            Uint32 TleftType = TregMemBuffer[theRegister];
            Int64 Tleft0 = *(Int64 *)(TregMemBuffer + theRegister + 2);

            if ((TleftType | TrightType) != 0) {
              Uint64 Tdest0 = Tleft0 + Tright0;
              *(Int64 *)(TregMemBuffer + TdestRegister + 2) = Tdest0;
              TregMemBuffer[TdestRegister] = 0x60;
            } else {
              return TUPKEY_abort(req_struct, 20);
            }
            break;
          }

        case Interpreter::SUB_REG_REG:
          jamDebug();
          {
            Uint32 TrightRegister = Interpreter::getReg2(theInstruction) << 2;
            Uint32 TdestRegister = Interpreter::getReg3(theInstruction) << 2;

            Uint32 TrightType = TregMemBuffer[TrightRegister];
            Int64 Tright0 = *(Int64 *)(TregMemBuffer + TrightRegister + 2);

            Uint32 TleftType = TregMemBuffer[theRegister];
            Int64 Tleft0 = *(Int64 *)(TregMemBuffer + theRegister + 2);

            if ((TleftType | TrightType) != 0) {
              Int64 Tdest0 = Tleft0 - Tright0;
              *(Int64 *)(TregMemBuffer + TdestRegister + 2) = Tdest0;
              TregMemBuffer[TdestRegister] = 0x60;
            } else {
              return TUPKEY_abort(req_struct, 20);
            }
            break;
          }

        case Interpreter::BRANCH:
          TprogramCounter = brancher(theInstruction, TprogramCounter);
          break;

        case Interpreter::BRANCH_REG_EQ_NULL:
          if (TregMemBuffer[theRegister] != 0) {
            jamDebug();
            continue;
          } else {
            jamDebug();
            TprogramCounter = brancher(theInstruction, TprogramCounter);
          }
          break;

        case Interpreter::BRANCH_REG_NE_NULL:
          if (TregMemBuffer[theRegister] == 0) {
            jamDebug();
            continue;
          } else {
            jamDebug();
            TprogramCounter = brancher(theInstruction, TprogramCounter);
          }
          break;

        case Interpreter::BRANCH_EQ_REG_REG: {
          jamDebug();
          Uint32 TrightRegister = Interpreter::getReg2(theInstruction) << 2;

          Uint32 TleftType = TregMemBuffer[theRegister];
          Uint32 Tleft0 = TregMemBuffer[theRegister + 2];
          Uint32 Tleft1 = TregMemBuffer[theRegister + 3];

          Uint32 TrightType = TregMemBuffer[TrightRegister];
          Uint32 Tright0 = TregMemBuffer[TrightRegister + 2];
          Uint32 Tright1 = TregMemBuffer[TrightRegister + 3];
          if ((TrightType | TleftType) != 0) {
            jamDebug();
            if ((Tleft0 == Tright0) && (Tleft1 == Tright1)) {
              TprogramCounter = brancher(theInstruction, TprogramCounter);
            }
          } else {
            return TUPKEY_abort(req_struct, 23);
          }
          break;
        }

        case Interpreter::BRANCH_NE_REG_REG: {
          Uint32 TrightRegister = Interpreter::getReg2(theInstruction) << 2;

          Uint32 TleftType = TregMemBuffer[theRegister];
          Uint32 Tleft0 = TregMemBuffer[theRegister + 2];
          Uint32 Tleft1 = TregMemBuffer[theRegister + 3];

          Uint32 TrightType = TregMemBuffer[TrightRegister];
          Uint32 Tright0 = TregMemBuffer[TrightRegister + 2];
          Uint32 Tright1 = TregMemBuffer[TrightRegister + 3];
          if ((TrightType | TleftType) != 0) {
            jamDebug();
            if ((Tleft0 != Tright0) || (Tleft1 != Tright1)) {
              TprogramCounter = brancher(theInstruction, TprogramCounter);
            }
          } else {
            return TUPKEY_abort(req_struct, 24);
          }
          break;
        }

        case Interpreter::BRANCH_LT_REG_REG: {
          Uint32 TrightRegister = Interpreter::getReg2(theInstruction) << 2;

          Uint32 TrightType = TregMemBuffer[TrightRegister];
          Int64 Tright0 = *(Int64 *)(TregMemBuffer + TrightRegister + 2);

          Uint32 TleftType = TregMemBuffer[theRegister];
          Int64 Tleft0 = *(Int64 *)(TregMemBuffer + theRegister + 2);

          if ((TrightType | TleftType) != 0) {
            jamDebug();
            if (Tleft0 < Tright0) {
              TprogramCounter = brancher(theInstruction, TprogramCounter);
            }
          } else {
            return TUPKEY_abort(req_struct, 24);
          }
          break;
        }

        case Interpreter::BRANCH_LE_REG_REG: {
          Uint32 TrightRegister = Interpreter::getReg2(theInstruction) << 2;

          Uint32 TrightType = TregMemBuffer[TrightRegister];
          Int64 Tright0 = *(Int64 *)(TregMemBuffer + TrightRegister + 2);

          Uint32 TleftType = TregMemBuffer[theRegister];
          Int64 Tleft0 = *(Int64 *)(TregMemBuffer + theRegister + 2);

          if ((TrightType | TleftType) != 0) {
            jamDebug();
            if (Tleft0 <= Tright0) {
              TprogramCounter = brancher(theInstruction, TprogramCounter);
            }
          } else {
            return TUPKEY_abort(req_struct, 26);
          }
          break;
        }

        case Interpreter::BRANCH_GT_REG_REG: {
          Uint32 TrightRegister = Interpreter::getReg2(theInstruction) << 2;

          Uint32 TrightType = TregMemBuffer[TrightRegister];
          Int64 Tright0 = *(Int64 *)(TregMemBuffer + TrightRegister + 2);

          Uint32 TleftType = TregMemBuffer[theRegister];
          Int64 Tleft0 = *(Int64 *)(TregMemBuffer + theRegister + 2);

          if ((TrightType | TleftType) != 0) {
            jamDebug();
            if (Tleft0 > Tright0) {
              TprogramCounter = brancher(theInstruction, TprogramCounter);
            }
          } else {
            return TUPKEY_abort(req_struct, 27);
          }
          break;
        }

        case Interpreter::BRANCH_GE_REG_REG: {
          Uint32 TrightRegister = Interpreter::getReg2(theInstruction) << 2;

          Uint32 TrightType = TregMemBuffer[TrightRegister];
          Int64 Tright0 = *(Int64 *)(TregMemBuffer + TrightRegister + 2);

          Uint32 TleftType = TregMemBuffer[theRegister];
          Int64 Tleft0 = *(Int64 *)(TregMemBuffer + theRegister + 2);

          if ((TrightType | TleftType) != 0) {
            jamDebug();
            if (Tleft0 >= Tright0) {
              TprogramCounter = brancher(theInstruction, TprogramCounter);
            }
          } else {
            return TUPKEY_abort(req_struct, 28);
          }
          break;
        }

        case Interpreter::BRANCH_ATTR_OP_ATTR:
        case Interpreter::BRANCH_ATTR_OP_PARAM:
        case Interpreter::BRANCH_ATTR_OP_ARG: {
          jamDebug();
          const Uint32 ins2 = TcurrentProgram[TprogramCounter];
          Uint32 attrId = Interpreter::getBranchCol_AttrId(ins2) << 16;
          const Uint32 opCode = Interpreter::getOpCode(theInstruction);

          if (tmpHabitant != attrId) {
            Int32 TnoDataR =
                readAttributes(req_struct, &attrId, 1, tmpArea, tmpAreaSz);

            if (unlikely(TnoDataR < 0)) {
              jam();
              terrorCode = Uint32(-TnoDataR);
              tupkeyErrorLab(req_struct);
              return -1;
            }
            tmpHabitant = attrId;
          }

          // get type
          attrId >>= 16;
          const Uint32 TattrDescrIndex =
              req_struct->tablePtrP->tabDescriptor + (attrId << ZAD_LOG_SIZE);
          const Uint32 TattrDesc1 = tableDescriptor[TattrDescrIndex].tabDescr;
          const Uint32 TattrDesc2 =
              tableDescriptor[TattrDescrIndex + 1].tabDescr;
          const Uint32 typeId = AttributeDescriptor::getType(TattrDesc1);
          const CHARSET_INFO *cs = nullptr;
          if (AttributeOffset::getCharsetFlag(TattrDesc2)) {
            const Uint32 pos = AttributeOffset::getCharsetPos(TattrDesc2);
            cs = req_struct->tablePtrP->charsetArray[pos];
          }
          const NdbSqlUtil::Type &sqlType = NdbSqlUtil::getType(typeId);

          // get data for 1st argument, always an ATTR.
          const AttributeHeader ah(tmpArea[0]);
          const char *s1 = (char *)&tmpArea[1];
          // fixed length in 5.0
          Uint32 attrLen = AttributeDescriptor::getSizeInBytes(TattrDesc1);
          if (unlikely(typeId == NDB_TYPE_BIT)) {
            /* Size in bytes for bit fields can be incorrect due to
             * rounding down
             */
            Uint32 bitFieldAttrLen =
                (AttributeDescriptor::getArraySize(TattrDesc1) + 7) / 8;
            attrLen = bitFieldAttrLen;
          }

          // 2'nd argument, literal, parameter or another attribute
          Uint32 argLen = 0;
          Uint32 step = 0;
          const char *s2 = nullptr;

          if (likely(opCode == Interpreter::BRANCH_ATTR_OP_ARG)) {
            // Compare ATTR with a literal value given by interpreter code
            jamDebug();
            argLen = Interpreter::getBranchCol_Len(ins2);
            step = argLen;
            s2 = (char *)&TcurrentProgram[TprogramCounter + 1];
          } else if (opCode == Interpreter::BRANCH_ATTR_OP_PARAM) {
            // Compare ATTR with a parameter
            jamDebug();
            ndbassert(req_struct != nullptr);
            ndbassert(req_struct->operPtrP != nullptr);

            const Uint32 paramNo = Interpreter::getBranchCol_ParamNo(ins2);
            const Uint32 *paramPos = subroutineProg;
            const Uint32 *paramptr =
                lookupInterpreterParameter(paramNo, paramPos);
            if (unlikely(paramptr == nullptr)) {
              jam();
              terrorCode = 99;  // TODO
              tupkeyErrorLab(req_struct);
              return -1;
            }

            argLen = AttributeHeader::getByteSize(*paramptr);
            step = 0;
            s2 = (char *)(paramptr + 1);
          } else if (opCode == Interpreter::BRANCH_ATTR_OP_ATTR) {
            // Compare ATTR with another ATTR
            jamDebug();
            Uint32 attr2Id = Interpreter::getBranchCol_AttrId2(ins2) << 16;

            // Attr2 to be read into tmpArea[] after Attr1.
            const Uint32 firstAttrWords = attrLen + 1;
            assert(tmpAreaSz >= 2 * firstAttrWords);
            Int32 TnoDataR = readAttributes(req_struct, &attr2Id, 1,
                                            &tmpArea[firstAttrWords],
                                            tmpAreaSz - firstAttrWords);
            if (unlikely(TnoDataR < 0)) {
              jam();
              terrorCode = Uint32(-TnoDataR);
              tupkeyErrorLab(req_struct);
              return -1;
            }

            const AttributeHeader ah2(tmpArea[firstAttrWords]);
            if (!ah2.isNULL()) {
              // Get type
              attr2Id >>= 16;
              const Uint32 Tattr2DescrIndex =
                  req_struct->tablePtrP->tabDescriptor +
                  (attr2Id << ZAD_LOG_SIZE);
              const Uint32 Tattr2Desc1 =
                  tableDescriptor[Tattr2DescrIndex].tabDescr;
              const Uint32 type2Id = AttributeDescriptor::getType(Tattr2Desc1);

              argLen = AttributeDescriptor::getSizeInBytes(Tattr2Desc1);
              if (unlikely(type2Id == NDB_TYPE_BIT)) {
                /* Size in bytes for bit fields can be incorrect due to
                 * rounding down
                 */
                Uint32 bitFieldAttrLen =
                    (AttributeDescriptor::getArraySize(Tattr2Desc1) + 7) / 8;
                argLen = bitFieldAttrLen;
              }
              s2 = (char *)&tmpArea[firstAttrWords + 1];
            }
            step = 0;
          }  //! ah2.isNULL()

          // Evaluate
          const bool r1_null = ah.isNULL();
          const bool r2_null = argLen == 0;
          if (r1_null || r2_null) {
            // There are NULL-valued operands, check the NullSemantics
            const Uint32 nullSemantics =
                Interpreter::getNullSemantics(theInstruction);
            if (nullSemantics == Interpreter::IF_NULL_BREAK_OUT) {
              // Branch out of AND conjunction
              TprogramCounter = brancher(theInstruction, TprogramCounter);
              break;
            }
            if (nullSemantics == Interpreter::IF_NULL_CONTINUE) {
              // Ignore NULL in OR conjunction,  -> next instruction
              const Uint32 tmp = ((step + 3) >> 2) + 1;
              TprogramCounter += tmp;
              break;
            }
          }

          const Uint32 cond = Interpreter::getBinaryCondition(theInstruction);
          int res1;
          if (cond <= Interpreter::GE) {
            /* Inequality - EQ, NE, LT, LE, GT, GE */
            if (r1_null || r2_null) {
              // NULL==NULL and NULL<not-NULL
              res1 = r1_null && r2_null ? 0 : r1_null ? -1 : 1;
            } else {
              jamDebug();
              if (unlikely(sqlType.m_cmp == 0)) {
                return TUPKEY_abort(req_struct, 40);
              }
              res1 = (*sqlType.m_cmp)(cs, s1, attrLen, s2, argLen);
            }
          } else {
            if ((cond == Interpreter::LIKE) ||
                (cond == Interpreter::NOT_LIKE)) {
              if (r1_null || r2_null) {
                // NULL like NULL is true (has no practical use)
                res1 = r1_null && r2_null ? 0 : -1;
              } else {
                jam();
                if (unlikely(sqlType.m_like == 0)) {
                  return TUPKEY_abort(req_struct, 40);
                }
                res1 = (*sqlType.m_like)(cs, s1, attrLen, s2, argLen);
              }
            } else {
              /* AND_XX_MASK condition */
              ndbassert(cond <= Interpreter::AND_NE_ZERO);
              if (unlikely(sqlType.m_mask == 0)) {
                return TUPKEY_abort(req_struct, 40);
              }
              /* If either arg is NULL, we say COL AND MASK
               * NE_ZERO and NE_MASK.
               */
              if (r1_null || r2_null) {
                res1 = 1;
              } else {
                bool cmpZero = (cond == Interpreter::AND_EQ_ZERO) ||
                               (cond == Interpreter::AND_NE_ZERO);

                res1 = (*sqlType.m_mask)(s1, attrLen, s2, argLen, cmpZero);
              }
            }
          }

          int res = 0;
          switch ((Interpreter::BinaryCondition)cond) {
            case Interpreter::EQ:
              res = (res1 == 0);
              break;
            case Interpreter::NE:
              res = (res1 != 0);
              break;
            // note the condition is backwards
            case Interpreter::LT:
              res = (res1 > 0);
              break;
            case Interpreter::LE:
              res = (res1 >= 0);
              break;
            case Interpreter::GT:
              res = (res1 < 0);
              break;
            case Interpreter::GE:
              res = (res1 <= 0);
              break;
            case Interpreter::LIKE:
              res = (res1 == 0);
              break;
            case Interpreter::NOT_LIKE:
              res = (res1 == 1);
              break;
            case Interpreter::AND_EQ_MASK:
              res = (res1 == 0);
              break;
            case Interpreter::AND_NE_MASK:
              res = (res1 != 0);
              break;
            case Interpreter::AND_EQ_ZERO:
              res = (res1 == 0);
              break;
            case Interpreter::AND_NE_ZERO:
              res = (res1 != 0);
              break;
              // XXX handle invalid value
          }
#ifdef TRACE_INTERPRETER
          g_eventLogger->info(
              "cond=%u attr(%d)='%.*s'(%d) str='%.*s'(%d) res1=%d res=%d", cond,
              attrId >> 16, attrLen, s1, attrLen, argLen, s2, argLen, res1,
              res);
#endif
          if (res)
            TprogramCounter = brancher(theInstruction, TprogramCounter);
          else {
            Uint32 tmp = ((step + 3) >> 2) + 1;
            TprogramCounter += tmp;
          }
          break;
        }

        case Interpreter::BRANCH_ATTR_EQ_NULL: {
          jamDebug();
          Uint32 ins2 = TcurrentProgram[TprogramCounter];
          Uint32 attrId = Interpreter::getBranchCol_AttrId(ins2) << 16;

          if (tmpHabitant != attrId) {
            Int32 TnoDataR =
                readAttributes(req_struct, &attrId, 1, tmpArea, tmpAreaSz);

            if (unlikely(TnoDataR < 0)) {
              jam();
              terrorCode = Uint32(-TnoDataR);
              tupkeyErrorLab(req_struct);
              return -1;
            }
            tmpHabitant = attrId;
          }

          AttributeHeader ah(tmpArea[0]);
          if (ah.isNULL()) {
            TprogramCounter = brancher(theInstruction, TprogramCounter);
          } else {
            TprogramCounter++;
          }
          break;
        }

        case Interpreter::BRANCH_ATTR_NE_NULL: {
          jamDebug();
          Uint32 ins2 = TcurrentProgram[TprogramCounter];
          Uint32 attrId = Interpreter::getBranchCol_AttrId(ins2) << 16;

          if (tmpHabitant != attrId) {
            Int32 TnoDataR =
                readAttributes(req_struct, &attrId, 1, tmpArea, tmpAreaSz);

            if (unlikely(TnoDataR < 0)) {
              jam();
              terrorCode = Uint32(-TnoDataR);
              tupkeyErrorLab(req_struct);
              return -1;
            }
            tmpHabitant = attrId;
          }

          AttributeHeader ah(tmpArea[0]);
          if (ah.isNULL()) {
            TprogramCounter++;
          } else {
            TprogramCounter = brancher(theInstruction, TprogramCounter);
          }
          break;
        }

        case Interpreter::EXIT_OK:
          jamDebug();
#ifdef TRACE_INTERPRETER
          g_eventLogger->info(" - exit_ok");
#endif
          return TdataWritten;

        case Interpreter::EXIT_OK_LAST:
          jamDebug();
#ifdef TRACE_INTERPRETER
          g_eventLogger->info(" - exit_ok_last");
#endif
          req_struct->last_row = true;
          return TdataWritten;

        case Interpreter::EXIT_REFUSE: {
          /**
           * This is a very common exit path, particularly
           * for scans. It simply means that the row didn't
           * fulfil the search condition.
           */
          jamDebug();
#ifdef TRACE_INTERPRETER
          g_eventLogger->info(" - exit_nok");
#endif
          terrorCode = theInstruction >> 16;
          tupkeyErrorLab(req_struct);
          return -1;
        }
        case Interpreter::CALL:
          jamDebug();
#ifdef TRACE_INTERPRETER
          g_eventLogger->info(" - call addr=%u, subroutine len=%u ret addr=%u",
                              theInstruction >> 16, TsubroutineLen,
                              TprogramCounter);
#endif
          RstackPtr++;
          if (RstackPtr < 32) {
            TstackMemBuffer[RstackPtr] = TprogramCounter;
            TprogramCounter = theInstruction >> 16;
            if (TprogramCounter < TsubroutineLen) {
              TcurrentProgram = subroutineProg;
              TcurrentSize = TsubroutineLen;
            } else {
              return TUPKEY_abort(req_struct, 30);
            }
          } else {
            return TUPKEY_abort(req_struct, 31);
          }
          break;

        case Interpreter::RETURN:
          jamDebug();
#ifdef TRACE_INTERPRETER
          g_eventLogger->info(" - return to %u from stack level %u",
                              TstackMemBuffer[RstackPtr], RstackPtr);
#endif
          if (RstackPtr > 0) {
            TprogramCounter = TstackMemBuffer[RstackPtr];
            RstackPtr--;
            if (RstackPtr == 0) {
              jamDebug();
              /* -------------------------------------------------------------
               */
              // We are back to the main program.
              /* -------------------------------------------------------------
               */
              TcurrentProgram = mainProgram;
              TcurrentSize = TmainProgLen;
            }
          } else {
            return TUPKEY_abort(req_struct, 32);
          }
          break;

        default:
          return TUPKEY_abort(req_struct, 33);
      }
    } else {
      return TUPKEY_abort(req_struct, 34);
    }
  }
  return TUPKEY_abort(req_struct, 35);
}