4227 jonas oreland 2011-02-23
ndb - more spj merging
added:
storage/ndb/include/kernel/signaldata/DbspjErr.hpp
storage/ndb/include/kernel/signaldata/QueryTree.hpp
storage/ndb/src/kernel/blocks/dbspj/
storage/ndb/src/kernel/blocks/dbspj/Dbspj.hpp
storage/ndb/src/kernel/blocks/dbspj/DbspjInit.cpp
storage/ndb/src/kernel/blocks/dbspj/DbspjMain.cpp
storage/ndb/src/kernel/blocks/dbspj/DbspjProxy.cpp
storage/ndb/src/kernel/blocks/dbspj/DbspjProxy.hpp
modified:
storage/ndb/include/kernel/signaldata/ScanFrag.hpp
storage/ndb/include/kernel/signaldata/SignalData.hpp
storage/ndb/src/common/debugger/BlockNames.cpp
storage/ndb/src/kernel/CMakeLists.txt
storage/ndb/src/kernel/Makefile.am
storage/ndb/src/kernel/SimBlockList.cpp
storage/ndb/src/kernel/blocks/CMakeLists.txt
storage/ndb/src/kernel/blocks/Makefile.am
storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp
storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp
storage/ndb/src/kernel/blocks/dbinfo/Dbinfo.cpp
storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp
storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp
storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp
storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp
storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp
storage/ndb/src/kernel/blocks/qmgr/Qmgr.hpp
storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp
storage/ndb/src/kernel/vm/mt.cpp
4226 Jonas Oreland 2011-02-23 [merge]
ndb merge 63 to 70
=== added file 'storage/ndb/include/kernel/signaldata/DbspjErr.hpp'
--- a/storage/ndb/include/kernel/signaldata/DbspjErr.hpp 1970-01-01 00:00:00 +0000
+++ b/storage/ndb/include/kernel/signaldata/DbspjErr.hpp 2011-02-23 19:28:26 +0000
@@ -0,0 +1,45 @@
+/*
+ Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef DBSPJ_ERR_H
+#define DBSPJ_ERR_H
+
+struct DbspjErr
+{
+ enum {
+ OutOfOperations = 20000
+ ,ZeroLengthQueryTree = 20001
+ ,InvalidRequest = 20002
+ ,UnknowQueryOperation = 20003
+ ,InvalidTreeNodeSpecification = 20004
+ ,InvalidTreeParametersSpecification = 20005
+ ,OutOfSectionMemory = 20006
+ ,InvalidPattern = 20007
+ ,OutOfQueryMemory = 20008
+ ,QueryNodeTooBig = 20009
+ ,QueryNodeParametersTooBig = 20010
+ ,BothTreeAndParametersContainInterpretedProgram = 20011
+ ,InvalidTreeParametersSpecificationKeyParamBitsMissmatch = 20012
+ ,InvalidTreeParametersSpecificationIncorrectKeyParamCount = 20013
+ ,InternalError = 20014
+ ,OutOfRowMemory = 20015
+ ,NodeFailure = 20016
+ ,InvalidTreeNodeCount = 20017
+ };
+};
+
+#endif
=== added file 'storage/ndb/include/kernel/signaldata/QueryTree.hpp'
--- a/storage/ndb/include/kernel/signaldata/QueryTree.hpp 1970-01-01 00:00:00 +0000
+++ b/storage/ndb/include/kernel/signaldata/QueryTree.hpp 2011-02-23 19:28:26 +0000
@@ -0,0 +1,365 @@
+/*
+ Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef NDB_QUERY_TREE_HPP
+#define NDB_QUERY_TREE_HPP
+
+#include <ndb_global.h>
+#include <ndb_types.h>
+
+struct QueryNode // Effectively used as a base class for QN_xxxNode
+{
+ Uint32 len;
+ Uint32 requestInfo;
+ Uint32 tableId; // 16-bit
+ Uint32 tableVersion;
+
+ enum OpType
+ {
+ QN_LOOKUP = 0x1,
+ QN_SCAN_FRAG = 0x2,
+ QN_SCAN_INDEX = 0x3,
+ QN_END = 0
+ };
+
+ static Uint32 getOpType(Uint32 op_len) { return op_len & 0xFFFF;}
+ static Uint32 getLength(Uint32 op_len) { return op_len >> 16;}
+
+ static void setOpLen(Uint32 &d, Uint32 o, Uint32 l) { d = (l << 16) | o;}
+
+ // If possible we should change the above static methods to non-static:
+//Uint32 getOpType() const { return len & 0xFFFF;}
+//Uint32 getLength() const { return len >> 16;}
+//void setOpLen(Uint32 o, Uint32 l) { len = (l << 16) | o;}
+};
+
+struct QueryNodeParameters // Effectively used as a base class for QN_xxxParameters
+{
+ Uint32 len;
+ Uint32 requestInfo;
+ Uint32 resultData; // Api connect ptr
+
+ enum OpType
+ {
+ QN_LOOKUP = 0x1,
+ QN_SCAN_FRAG = 0x2,
+ QN_SCAN_INDEX = 0x3,
+ QN_END = 0
+ };
+
+ static Uint32 getOpType(Uint32 op_len) { return op_len & 0xFFFF;}
+ static Uint32 getLength(Uint32 op_len) { return op_len >> 16;}
+
+ static void setOpLen(Uint32 &d, Uint32 o, Uint32 l) { d = (l << 16) | o;}
+
+ // If possible we should change the above static methods to non-static:
+//Uint32 getOpType() const { return len & 0xFFFF;}
+//Uint32 getLength() const { return len >> 16;}
+//void setOpLen(Uint32 o, Uint32 l) { len = (l << 16) | o;}
+};
+
+struct DABits
+{
+ /**
+ * List of requestInfo bits shared for QN_LookupNode,
+ * QN_ScanFragNode & QN_ScanIndexNode
+ */
+ enum NodeInfoBits
+ {
+ NI_HAS_PARENT = 0x01,
+
+ NI_KEY_LINKED = 0x02, // Does keyinfo contain linked values
+ NI_KEY_PARAMS = 0x04, // Does keyinfo contain parameters
+ NI_KEY_CONSTS = 0x08, // Does keyinfo contain const values
+
+ NI_LINKED_ATTR = 0x10, // List of attributes to be used by children
+
+ NI_ATTR_INTERPRET = 0x20, // Is attr-info a interpreted program
+ NI_ATTR_PARAMS = 0x40, // Does attrinfo contain parameters
+ NI_ATTR_LINKED = 0x80, // Does attrinfo contain linked values
+
+ /**
+ * Iff this flag is set, then this operation has a child operation with a
+ * linked value that refes to a disk column of this operation. For example
+ * SELECT * FROM t1, t2 WHERE t1.disk_att = t2.primary_key;
+ */
+ NI_LINKED_DISK = 0x100,
+
+ NI_END = 0
+ };
+
+ /**
+ * List of requestInfo bits shared for QN_LookupParameters,
+ * QN_ScanFragParameters & QN_ScanIndexParameters
+ */
+ enum ParamInfoBits
+ {
+ PI_ATTR_LIST = 0x1, // "user" projection list
+
+ /**
+ * These 2 must match their resp. QueryNode-definitions
+ */
+ PI_ATTR_PARAMS = 0x2, // attr-info parameters (NI_ATTR_PARAMS)
+ PI_KEY_PARAMS = 0x4, // key-info parameters (NI_KEY_PARAMS)
+
+ /**
+ * The parameter object contains a program that will be interpreted
+ * before reading the attributes (i.e. a scan filter).
+ * NOTE: Can/should not be used if QueryNode contains interpreted program
+ */
+ PI_ATTR_INTERPRET = 0x8,
+
+ /**
+ * Iff this flag is set, then the user projection for this operation
+ * contains at least one disk column.
+ */
+ PI_DISK_ATTR = 0x10,
+ PI_END = 0
+ };
+};
+
+
+/**
+ * This node describes a pk-lookup
+ */
+struct QN_LookupNode // Is a QueryNode subclass
+{
+ Uint32 len;
+ Uint32 requestInfo;
+ Uint32 tableId; // 16-bit
+ Uint32 tableVersion;
+ STATIC_CONST ( NodeSize = 4 );
+
+ /**
+ * See DABits::NodeInfoBits
+ */
+ Uint32 optional[1];
+
+ enum LookupBits
+ {
+ /**
+ * This is lookup on index table,
+ */
+ L_UNIQUE_INDEX = 0x10000,
+
+ L_END = 0
+ };
+
+//Uint32 getLength() const { return len >> 16;}
+//void setOpLen(Uint32 o, Uint32 l) { len = (l << 16) | o;}
+};
+
+/**
+ * This struct describes parameters that are associated with
+ * a QN_LookupNode
+ */
+struct QN_LookupParameters // Is a QueryNodeParameters subclass
+{
+ Uint32 len;
+ Uint32 requestInfo;
+ Uint32 resultData; // Api connect ptr
+ STATIC_CONST ( NodeSize = 3 );
+
+ /**
+ * See DABits::ParamInfoBits
+ */
+ Uint32 optional[1];
+};
+
+/**
+ * This node describes a table/index-fragment scan
+ */
+struct QN_ScanFragNode // Is a QueryNode subclass
+{
+ Uint32 len;
+ Uint32 requestInfo;
+ Uint32 tableId; // 16-bit
+ Uint32 tableVersion;
+ STATIC_CONST ( NodeSize = 4 );
+
+ /**
+ * See DABits::NodeInfoBits
+ */
+ Uint32 optional[1];
+};
+
+/**
+ * This struct describes parameters that are associated with
+ * a QN_ScanFragNode
+ */
+struct QN_ScanFragParameters // Is a QueryNodeParameters subclass
+{
+ Uint32 len;
+ Uint32 requestInfo;
+ Uint32 resultData; // Api connect ptr
+ STATIC_CONST ( NodeSize = 3 );
+
+ /**
+ * See DABits::ParamInfoBits
+ */
+ Uint32 optional[1];
+};
+
+/**
+ * This node describes a IndexScan
+ */
+struct QN_ScanIndexNode
+{
+ Uint32 len;
+ Uint32 requestInfo;
+ Uint32 tableId; // 16-bit
+ Uint32 tableVersion;
+ STATIC_CONST( NodeSize = 4 );
+
+ enum ScanIndexBits
+ {
+ /**
+ * If doing equality search that can be pruned
+ * a pattern that creates the key to hash with is stored before
+ * the DA optional part
+ */
+ SI_PRUNE_PATTERN = 0x10000,
+
+ // Do pattern contain parameters
+ SI_PRUNE_PARAMS = 0x20000,
+
+ // Is prune pattern dependant on parent key (or only on parameters / constants)
+ SI_PRUNE_LINKED = 0x40000,
+
+ // Should it be parallel scan (can also be set as in parameters)
+ SI_PARALLEL = 0x80000,
+
+ SI_END = 0
+ };
+
+ /**
+ * See DABits::NodeInfoBits
+ */
+ Uint32 optional[1];
+};
+
+/**
+ * This struct describes parameters that are associated with
+ * a QN_ScanIndexNode
+ */
+struct QN_ScanIndexParameters
+{
+ Uint32 len;
+ Uint32 requestInfo;
+ Uint32 batchSize; // (bytes << 16) | (rows)
+ Uint32 resultData; // Api connect ptr
+ STATIC_CONST ( NodeSize = 4 );
+
+ enum ScanIndexParamBits
+ {
+ /**
+ * Do arguments contain parameters for prune-pattern
+ */
+ SIP_PRUNE_PARAMS = 0x10000,
+
+ /**
+ * Should it scan index in parallel
+ * This is needed for "multi-cursor" semantics
+ * with (partial) ordering
+ */
+ SIP_PARALLEL = 0x20000,
+
+ SIP_END = 0
+ };
+
+ /**
+ * See DABits::ParamInfoBits
+ */
+ Uint32 optional[1];
+};
+
+/**
+ * This is the definition of a QueryTree
+ */
+struct QueryTree
+{
+ Uint32 cnt_len; // Length in words describing full tree + #nodes
+ Uint32 nodes[1]; // The nodes
+
+ static Uint32 getNodeCnt(Uint32 cnt_len) { return cnt_len & 0xFFFF;}
+ static Uint32 getLength(Uint32 cnt_len) { return cnt_len >> 16;}
+ static void setCntLen(Uint32 &d, Uint32 c, Uint32 l) { d=(l << 16) | c;}
+};
+
+/**
+ * This is description of *one* entry in a QueryPattern
+ * (used by various QueryNodes)
+ */
+struct QueryPattern
+{
+ Uint32 m_info;
+ enum
+ {
+ P_DATA = 0x1, // Raw data of len-words (constants)
+ P_COL = 0x2, // Get column value from RowRef
+ P_UNQ_PK = 0x3, // NDB$PK column from a unique index
+ P_PARAM = 0x4, // User specified parameter value
+ P_PARENT = 0x5, // Move up in tree
+ P_PARAM_HEADER = 0x6, // User specified param val including AttributeHeader
+ P_ATTRINFO = 0x7,// Get column including header from RowRef
+ P_END = 0
+ };
+
+ static Uint32 getType(const Uint32 info) { return info >> 16;}
+
+ /**
+ * If type == DATA, get len here
+ */
+ static Uint32 getLength(Uint32 info) { return info & 0xFFFF;}
+ static Uint32 data(Uint32 length)
+ {
+ assert(length <= 0xFFFF);
+ return (P_DATA << 16) | length;
+ }
+
+ /**
+ * If type == COL, get col-no here (index in row)
+ */
+ static Uint32 getColNo(Uint32 info) { return info & 0xFFFF;}
+ static Uint32 col(Uint32 no) { return (P_COL << 16) | no; }
+
+ /**
+ * If type == P_UNQ_PK, get PK value from composite NDB$PK col.
+ */
+ static Uint32 colPk(Uint32 no) { return (P_UNQ_PK << 16) | no; }
+
+ /**
+ * If type == PARAM, get param-no here (index in param list)
+ */
+ static Uint32 getParamNo(Uint32 info) { return info & 0xFFFF;}
+ static Uint32 param(Uint32 no) { return (P_PARAM << 16) | no; }
+
+ static Uint32 paramHeader(Uint32 no) { return (P_PARAM_HEADER << 16) | no; }
+
+ /**
+ * get col including header
+ */
+ static Uint32 attrInfo(Uint32 no) { return (P_ATTRINFO << 16) | no;}
+
+ /**
+ * Move to grand-parent no
+ * (0 == imediate parent)
+ */
+ static Uint32 parent(Uint32 no) { return (P_PARENT << 16) | no;}
+};
+
+#endif
=== modified file 'storage/ndb/include/kernel/signaldata/ScanFrag.hpp'
--- a/storage/ndb/include/kernel/signaldata/ScanFrag.hpp 2011-02-08 13:55:54 +0000
+++ b/storage/ndb/include/kernel/signaldata/ScanFrag.hpp 2011-02-23 19:28:26 +0000
@@ -235,7 +235,7 @@ class ScanFragNextReq {
Uint32 len, Uint16 receiverBlockNo);
public:
STATIC_CONST( SignalLength = 6 );
-
+
public:
Uint32 senderData;
Uint32 requestInfo; // 1 == close
@@ -243,11 +243,15 @@ public:
Uint32 transId2;
Uint32 batch_size_rows;
Uint32 batch_size_bytes;
+ Uint32 variableData[1];
STATIC_CONST( ZCLOSE = 1 );
- Uint32 getCloseFlag(const Uint32&);
- void setCloseFlag(Uint32&, Uint32);
+ static Uint32 getCloseFlag(const Uint32&);
+ static void setCloseFlag(Uint32&, Uint32);
+
+ static Uint32 getCorrFactorFlag(const Uint32&);
+ static void setCorrFactorFlag(Uint32&);
};
/**
@@ -484,4 +488,31 @@ ScanFragReq::setCorrFactorFlag(UintR & r
requestInfo |= (val << SF_CORR_FACTOR_SHIFT);
}
+/**
+ * Request Info (SCAN_NEXTREQ)
+ *
+ * c = close - 1 Bit 0
+ * C = corr value flag - 1 Bit 1
+ *
+ * 1111111111222222222233
+ * 01234567890123456789012345678901
+ * cC
+ */
+#define SFN_CLOSE_SHIFT 0
+#define SFN_CORR_SHIFT 1
+
+inline
+Uint32
+ScanFragNextReq::getCorrFactorFlag(const Uint32 & ri)
+{
+ return (ri >> SFN_CORR_SHIFT) & 1;
+}
+
+inline
+void
+ScanFragNextReq::setCorrFactorFlag(Uint32 & ri)
+{
+ ri |= (1 << SFN_CORR_SHIFT);
+}
+
#endif
=== modified file 'storage/ndb/include/kernel/signaldata/SignalData.hpp'
--- a/storage/ndb/include/kernel/signaldata/SignalData.hpp 2011-02-08 13:55:54 +0000
+++ b/storage/ndb/include/kernel/signaldata/SignalData.hpp 2011-02-23 19:28:26 +0000
@@ -171,6 +171,7 @@ GSN_PRINT_SIGNATURE(printSCANTABREQ);
GSN_PRINT_SIGNATURE(printSCANTABCONF);
GSN_PRINT_SIGNATURE(printSCANTABREF);
GSN_PRINT_SIGNATURE(printSCANNEXTREQ);
+GSN_PRINT_SIGNATURE(printSCANFRAGNEXTREQ);
GSN_PRINT_SIGNATURE(printLQH_FRAG_REQ);
GSN_PRINT_SIGNATURE(printLQH_FRAG_REF);
GSN_PRINT_SIGNATURE(printLQH_FRAG_CONF);
=== modified file 'storage/ndb/src/common/debugger/BlockNames.cpp'
--- a/storage/ndb/src/common/debugger/BlockNames.cpp 2011-02-01 23:27:25 +0000
+++ b/storage/ndb/src/common/debugger/BlockNames.cpp 2011-02-23 19:28:26 +0000
@@ -40,6 +40,7 @@ const BlockName BlockNames[] = {
,{ "PGMAN", PGMAN }
,{ "RESTORE", RESTORE }
,{ "DBINFO", DBINFO }
+ ,{ "DBSPJ", DBSPJ }
};
const BlockNumber NO_OF_BLOCK_NAMES = sizeof(BlockNames) / sizeof(BlockName);
=== modified file 'storage/ndb/src/kernel/CMakeLists.txt'
--- a/storage/ndb/src/kernel/CMakeLists.txt 2011-02-03 14:20:36 +0000
+++ b/storage/ndb/src/kernel/CMakeLists.txt 2011-02-23 19:28:26 +0000
@@ -36,6 +36,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOUR
${CMAKE_CURRENT_SOURCE_DIR}/blocks/suma
${CMAKE_CURRENT_SOURCE_DIR}/blocks/dbtux
${CMAKE_CURRENT_SOURCE_DIR}/blocks/dbinfo
+ ${CMAKE_CURRENT_SOURCE_DIR}/blocks/dbspj
${CMAKE_SOURCE_DIR}/storage/ndb/src/mgmsrv
${CMAKE_SOURCE_DIR}/storage/ndb/src/mgmapi
)
=== modified file 'storage/ndb/src/kernel/Makefile.am'
--- a/storage/ndb/src/kernel/Makefile.am 2011-02-01 23:27:25 +0000
+++ b/storage/ndb/src/kernel/Makefile.am 2011-02-23 19:28:26 +0000
@@ -38,6 +38,7 @@ INCLUDES += \
-I$(srcdir)/blocks/dbdict \
-I$(srcdir)/blocks/dbdih \
-I$(srcdir)/blocks/dblqh \
+ -I$(srcdir)/blocks/dbspj \
-I$(srcdir)/blocks/dbtc \
-I$(srcdir)/blocks/dbtup \
-I$(srcdir)/blocks/ndbfs \
=== modified file 'storage/ndb/src/kernel/SimBlockList.cpp'
--- a/storage/ndb/src/kernel/SimBlockList.cpp 2011-02-08 13:55:54 +0000
+++ b/storage/ndb/src/kernel/SimBlockList.cpp 2011-02-23 19:28:26 +0000
@@ -24,6 +24,7 @@
#include <Dbdict.hpp>
#include <Dbdih.hpp>
#include <Dblqh.hpp>
+#include <Dbspj.hpp>
#include <Dbtc.hpp>
#include <Dbtup.hpp>
#include <Ndbcntr.hpp>
@@ -41,6 +42,7 @@
#include <NdbEnv.h>
#include <LocalProxy.hpp>
#include <DblqhProxy.hpp>
+#include <DbspjProxy.hpp>
#include <DbaccProxy.hpp>
#include <DbtupProxy.hpp>
#include <DbtuxProxy.hpp>
@@ -84,7 +86,7 @@ void * operator new (size_t sz, SIMBLOCK
void
SimBlockList::load(EmulatorData& data){
- noOfBlocks = NO_OF_BLOCKS - /* SPJ */ 1;
+ noOfBlocks = NO_OF_BLOCKS;
theList = new SimulatedBlock * [noOfBlocks];
if (!theList)
{
@@ -149,7 +151,8 @@ SimBlockList::load(EmulatorData& data){
else
theList[18] = NEW_BLOCK(RestoreProxy)(ctx);
theList[19] = NEW_BLOCK(Dbinfo)(ctx);
- assert(NO_OF_BLOCKS == 20 + /** SPJ */ 1);
+ theList[20] = NEW_BLOCK(Dbspj)(ctx);
+ assert(NO_OF_BLOCKS == 21);
if (globalData.isNdbMt) {
add_main_thr_map();
=== modified file 'storage/ndb/src/kernel/blocks/CMakeLists.txt'
--- a/storage/ndb/src/kernel/blocks/CMakeLists.txt 2011-02-03 14:20:36 +0000
+++ b/storage/ndb/src/kernel/blocks/CMakeLists.txt 2011-02-23 19:28:26 +0000
@@ -35,6 +35,7 @@ ADD_LIBRARY(ndbblocks STATIC
dbdih/DbdihInit.cpp dbdih/DbdihMain.cpp
dblqh/DblqhInit.cpp dblqh/DblqhMain.cpp
dbtc/DbtcInit.cpp dbtc/DbtcMain.cpp
+ dbspj/DbspjInit.cpp dbspj/DbspjMain.cpp dbspj/DbspjProxy.cpp
dbtup/DbtupExecQuery.cpp dbtup/DbtupBuffer.cpp
dbtup/DbtupRoutines.cpp dbtup/DbtupCommit.cpp
dbtup/DbtupFixAlloc.cpp dbtup/DbtupTrigger.cpp
=== modified file 'storage/ndb/src/kernel/blocks/Makefile.am'
--- a/storage/ndb/src/kernel/blocks/Makefile.am 2011-02-01 23:27:25 +0000
+++ b/storage/ndb/src/kernel/blocks/Makefile.am 2011-02-23 19:28:26 +0000
@@ -30,6 +30,7 @@ libblocks_a_SOURCES = tsman.cpp lgman.cp
dbdict/Dbdict.cpp \
dbdih/DbdihInit.cpp dbdih/DbdihMain.cpp \
dblqh/DblqhInit.cpp dblqh/DblqhMain.cpp \
+ dbspj/DbspjInit.cpp dbspj/DbspjMain.cpp dbspj/DbspjProxy.cpp \
dbtc/DbtcInit.cpp dbtc/DbtcMain.cpp \
dbtup/DbtupExecQuery.cpp dbtup/DbtupBuffer.cpp \
dbtup/DbtupRoutines.cpp dbtup/DbtupCommit.cpp \
=== modified file 'storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp'
--- a/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp 2011-02-01 23:27:25 +0000
+++ b/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp 2011-02-23 19:28:26 +0000
@@ -245,6 +245,7 @@ static Uint32 blocks[] =
TSMAN_REF,
PGMAN_REF,
DBINFO_REF,
+ DBSPJ_REF,
0
};
=== modified file 'storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp'
--- a/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp 2011-02-19 10:31:42 +0000
+++ b/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp 2011-02-23 19:28:26 +0000
@@ -2660,13 +2660,14 @@ void Dbdih::execINCL_NODECONF(Signal* si
Uint32 TstartNode = signal->theData[0];
Uint32 TsendNodeId_or_blockref = signal->theData[1];
- Uint32 blocklist[6];
+ Uint32 blocklist[7];
blocklist[0] = clocallqhblockref;
blocklist[1] = clocaltcblockref;
blocklist[2] = cdictblockref;
blocklist[3] = numberToRef(BACKUP, getOwnNodeId());
blocklist[4] = numberToRef(SUMA, getOwnNodeId());
- blocklist[5] = 0;
+ blocklist[5] = numberToRef(DBSPJ, getOwnNodeId());
+ blocklist[6] = 0;
for (Uint32 i = 0; blocklist[i] != 0; i++)
{
=== modified file 'storage/ndb/src/kernel/blocks/dbinfo/Dbinfo.cpp'
--- a/storage/ndb/src/kernel/blocks/dbinfo/Dbinfo.cpp 2011-02-02 00:40:07 +0000
+++ b/storage/ndb/src/kernel/blocks/dbinfo/Dbinfo.cpp 2011-02-23 19:28:26 +0000
@@ -29,7 +29,7 @@
Uint32 dbinfo_blocks[] = { DBACC, DBTUP, BACKUP, DBTC, SUMA, DBUTIL,
TRIX, DBTUX, DBDICT, CMVMI, DBLQH, LGMAN,
- PGMAN, 0};
+ PGMAN, DBSPJ, 0};
Dbinfo::Dbinfo(Block_context& ctx) :
SimulatedBlock(DBINFO, ctx)
=== modified file 'storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp'
--- a/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp 2011-02-15 11:41:27 +0000
+++ b/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp 2011-02-23 19:28:26 +0000
@@ -2061,6 +2061,7 @@ public:
UintR simpleTcConnect;
UintR tableref;
UintR tcOprec;
+ Uint32 tcHashKeyHi;
UintR tcScanInfo;
UintR tcScanRec;
UintR totReclenAi;
@@ -2081,6 +2082,8 @@ public:
Uint32 m_scan_curr_range_no;
UintR noFiredTriggers;
};
+ Uint32 m_corrFactorLo; // For result correlation for linked operations.
+ Uint32 m_corrFactorHi;
Uint64 lqhKeyReqId;
Uint16 errorCode;
Uint16 logStartPageIndex;
@@ -2346,7 +2349,8 @@ private:
Uint32 transid1,
Uint32 transid2,
Uint32 fragId,
- Uint32 nodeId);
+ Uint32 nodeId,
+ Uint32 hashHi);
void finishScanrec(Signal* signal);
void releaseScanrec(Signal* signal);
void seizeScanrec(Signal* signal);
@@ -2406,7 +2410,7 @@ private:
LogPartRecordPtr flfLogPartPtr,
LogFileRecordPtr* parLogFilePtr);
void findPageRef(Signal* signal, CommitLogRecord* commitLogRecord);
- int findTransaction(UintR Transid1, UintR Transid2, UintR TcOprec);
+ int findTransaction(UintR Transid1, UintR Transid2, UintR TcOprec, UintR hi);
void getFirstInLogQueue(Signal* signal, Ptr<TcConnectionrec>&dst);
bool getFragmentrec(Signal* signal, Uint32 fragId);
void initialiseAddfragrec(Signal* signal);
=== modified file 'storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp'
--- a/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp 2011-02-19 10:31:42 +0000
+++ b/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp 2011-02-23 19:28:26 +0000
@@ -2937,7 +2937,8 @@ void Dblqh::noFreeRecordLab(Signal* sign
}
if (LqhKeyReq::getDirtyFlag(reqInfo) &&
- LqhKeyReq::getOperation(reqInfo) == ZREAD){
+ LqhKeyReq::getOperation(reqInfo) == ZREAD &&
+ !LqhKeyReq::getNormalProtocolFlag(reqInfo)){
jam();
/* Dirty read sends TCKEYREF direct to client, and nothing to TC */
ndbrequire(LqhKeyReq::getApplicationAddressFlag(reqInfo));
@@ -3135,7 +3136,7 @@ void Dblqh::execLQHKEYREF(Signal* signal
* not find an easy way to modify the code so that findTransaction
* is usable also for them
*/
- if (findTransaction(transid1, transid2, tcOprec) != ZOK)
+ if (findTransaction(transid1, transid2, tcOprec, 0) != ZOK)
{
jam();
warningReport(signal, 14);
@@ -3397,6 +3398,21 @@ Dblqh::execREAD_PSEUDO_REQ(Signal* signa
memcpy(signal->theData, ®TcPtr.p->lqhKeyReqId, 8);
break;
}
+ case AttributeHeader::CORR_FACTOR64:
+ {
+ Uint32 add = 0;
+ ScanRecordPtr tmp;
+ tmp.i = regTcPtr.p->tcScanRec;
+ if (tmp.i != RNIL)
+ {
+ c_scanRecordPool.getPtr(tmp);
+ add = tmp.p->m_curr_batch_size_rows;
+ }
+
+ signal->theData[0] = regTcPtr.p->m_corrFactorLo + add;
+ signal->theData[1] = regTcPtr.p->m_corrFactorHi;
+ break;
+ }
default:
ndbrequire(false);
}
@@ -3798,7 +3814,8 @@ void Dblqh::sendLqhkeyconfTc(Signal* sig
if(!packed)
{
lqhKeyConf->connectPtr = tcConnectptr.i;
- if(Thostptr.i == 0 || Thostptr.i == getOwnNodeId())
+ if (instance() == refToInstance(atcBlockref) &&
+ (Thostptr.i == 0 || Thostptr.i == getOwnNodeId()))
{
/**
* This EXECUTE_DIRECT is multi-thread safe, as we only get here
@@ -3826,7 +3843,7 @@ void Dblqh::execKEYINFO(Signal* signal)
Uint32 transid1 = signal->theData[1];
Uint32 transid2 = signal->theData[2];
jamEntry();
- if (findTransaction(transid1, transid2, tcOprec) != ZOK) {
+ if (findTransaction(transid1, transid2, tcOprec, 0) != ZOK) {
jam();
return;
}//if
@@ -3933,7 +3950,7 @@ void Dblqh::execATTRINFO(Signal* signal)
jamEntry();
if (findTransaction(transid1,
transid2,
- tcOprec) != ZOK) {
+ tcOprec, 0) != ZOK) {
jam();
return;
}//if
@@ -4098,7 +4115,8 @@ void Dblqh::lqhAttrinfoLab(Signal* signa
/* ------ FIND TRANSACTION BY USING HASH TABLE ------- */
/* */
/* ------------------------------------------------------------------------- */
-int Dblqh::findTransaction(UintR Transid1, UintR Transid2, UintR TcOprec)
+int Dblqh::findTransaction(UintR Transid1, UintR Transid2, UintR TcOprec,
+ Uint32 hi)
{
TcConnectionrec *regTcConnectionrec = tcConnectionrec;
Uint32 ttcConnectrecFileSize = ctcConnectrecFileSize;
@@ -4110,7 +4128,8 @@ int Dblqh::findTransaction(UintR Transid
ptrCheckGuard(locTcConnectptr, ttcConnectrecFileSize, regTcConnectionrec);
if ((locTcConnectptr.p->transid[0] == Transid1) &&
(locTcConnectptr.p->transid[1] == Transid2) &&
- (locTcConnectptr.p->tcOprec == TcOprec)) {
+ (locTcConnectptr.p->tcOprec == TcOprec) &&
+ (locTcConnectptr.p->tcHashKeyHi == hi)) {
/* FIRST PART OF TRANSACTION CORRECT */
/* SECOND PART ALSO CORRECT */
/* THE OPERATION RECORD POINTER IN TC WAS ALSO CORRECT */
@@ -4309,16 +4328,13 @@ void Dblqh::execLQHKEYREQ(Signal* signal
}
sig0 = lqhKeyReq->clientConnectPtr;
- if (cfirstfreeTcConrec != RNIL && !ERROR_INSERTED(5031)) {
+ if (cfirstfreeTcConrec != RNIL && !ERROR_INSERTED_CLEAR(5031)) {
jamEntry();
seizeTcrec();
} else {
/* ------------------------------------------------------------------------- */
/* NO FREE TC RECORD AVAILABLE, THUS WE CANNOT HANDLE THE REQUEST. */
/* ------------------------------------------------------------------------- */
- if (ERROR_INSERTED(5031)) {
- CLEAR_ERROR_INSERT_VALUE;
- }
releaseSections(handle);
noFreeRecordLab(signal, lqhKeyReq, ZNO_TC_CONNECT_ERROR);
return;
@@ -4339,6 +4355,7 @@ void Dblqh::execLQHKEYREQ(Signal* signal
Uint32 senderRef = regTcPtr->clientBlockref = signal->senderBlockRef();
regTcPtr->clientConnectrec = sig0;
regTcPtr->tcOprec = sig0;
+ regTcPtr->tcHashKeyHi = 0;
regTcPtr->storedProcId = ZNIL;
regTcPtr->lqhKeyReqId = cTotalLqhKeyReqCount;
regTcPtr->m_flags= 0;
@@ -4509,6 +4526,17 @@ void Dblqh::execLQHKEYREQ(Signal* signal
nextPos++;
}//if
+ Uint32 TanyValueFlag = LqhKeyReq::getCorrFactorFlag(Treqinfo);
+ if (isLongReq && TanyValueFlag == 1)
+ {
+ /**
+ * For short lqhkeyreq, ai-length in-signal is stored in same pos...
+ */
+ regTcPtr->m_corrFactorLo = lqhKeyReq->variableData[nextPos + 0];
+ regTcPtr->m_corrFactorHi = lqhKeyReq->variableData[nextPos + 1];
+ nextPos += 2;
+ }
+
UintR TitcKeyLen = 0;
Uint32 keyLenWithLQHReq = 0;
UintR TreclenAiLqhkey = 0;
@@ -4577,12 +4605,15 @@ void Dblqh::execLQHKEYREQ(Signal* signal
regTcPtr->primKeyLen = TitcKeyLen;
/* Only node restart copy allowed to send no KeyInfo */
- if (( keyLenWithLQHReq == 0 ) &&
- (! (LqhKeyReq::getNrCopyFlag(Treqinfo))))
+ if (unlikely(keyLenWithLQHReq == 0))
{
- LQHKEY_error(signal, 3);
- return;
- }//if
+ if (! (LqhKeyReq::getNrCopyFlag(Treqinfo)) &&
+ refToMain(senderRef) != DBSPJ)
+ {
+ LQHKEY_error(signal, 3);
+ return;
+ }//if
+ }
sig0 = lqhKeyReq->variableData[nextPos + 0];
sig1 = lqhKeyReq->variableData[nextPos + 1];
@@ -5596,7 +5627,8 @@ void Dblqh::handleUserUnlockRequest(Sign
*/
if (unlikely( findTransaction(regTcPtr->transid[0],
regTcPtr->transid[1],
- tcOpRecIndex) != ZOK))
+ tcOpRecIndex,
+ 0) != ZOK))
{
jam();
unlockError(signal, ZBAD_OP_REF);
@@ -7349,7 +7381,7 @@ void Dblqh::execCOMMITREQ(Signal* signal
}//if
if (findTransaction(transid1,
transid2,
- tcOprec) != ZOK) {
+ tcOprec, 0) != ZOK) {
warningReport(signal, 5);
return;
}//if
@@ -7505,7 +7537,7 @@ void Dblqh::execCOMPLETEREQ(Signal* sign
}//if
if (findTransaction(transid1,
transid2,
- tcOprec) != ZOK) {
+ tcOprec, 0) != ZOK) {
jam();
/*---------------------------------------------------------*/
/* FOR SOME REASON THE COMPLETE PHASE STARTED AFTER */
@@ -7793,6 +7825,8 @@ void Dblqh::commitContinueAfterBlockedLa
Uint32 operation = regTcPtr.p->operation;
Uint32 dirtyOp = regTcPtr.p->dirtyOp;
Uint32 opSimple = regTcPtr.p->opSimple;
+ Uint32 normalProtocol = LqhKeyReq::getNormalProtocolFlag(regTcPtr.p->reqinfo);
+
if (regTcPtr.p->activeCreat != Fragrecord::AC_IGNORED) {
if (operation != ZREAD) {
TupCommitReq * const tupCommitReq =
@@ -7855,7 +7889,7 @@ void Dblqh::commitContinueAfterBlockedLa
EXECUTE_DIRECT(acc, GSN_ACC_COMMITREQ, signal, 1);
}
- if (dirtyOp)
+ if (dirtyOp && normalProtocol == 0)
{
jam();
/**
@@ -8171,7 +8205,7 @@ void Dblqh::execABORT(Signal* signal)
}//if
if (findTransaction(transid1,
transid2,
- tcOprec) != ZOK) {
+ tcOprec, 0) != ZOK) {
jam();
if(ERROR_INSERTED(5039) &&
@@ -8263,7 +8297,7 @@ void Dblqh::execABORTREQ(Signal* signal)
}//if
if (findTransaction(transid1,
transid2,
- tcOprec) != ZOK) {
+ tcOprec, 0) != ZOK) {
signal->theData[0] = reqPtr;
signal->theData[2] = cownNodeid;
signal->theData[3] = transid1;
@@ -8759,7 +8793,8 @@ void Dblqh::continueAfterLogAbortWriteLa
remove_commit_marker(regTcPtr);
- if (regTcPtr->operation == ZREAD && regTcPtr->dirtyOp)
+ if (regTcPtr->operation == ZREAD && regTcPtr->dirtyOp &&
+ !LqhKeyReq::getNormalProtocolFlag(regTcPtr->reqinfo))
{
jam();
TcKeyRef * const tcKeyRef = (TcKeyRef *) signal->getDataPtrSend();
@@ -8768,7 +8803,7 @@ void Dblqh::continueAfterLogAbortWriteLa
tcKeyRef->transId[0] = regTcPtr->transid[0];
tcKeyRef->transId[1] = regTcPtr->transid[1];
tcKeyRef->errorCode = regTcPtr->errorCode;
- sendTCKEYREF(signal, regTcPtr->applRef, regTcPtr->clientBlockref, 0);
+ sendTCKEYREF(signal, regTcPtr->applRef, regTcPtr->tcBlockref, 0);
cleanUp(signal);
return;
}//if
@@ -9464,8 +9499,9 @@ void Dblqh::execSCAN_NEXTREQ(Signal* sig
const Uint32 transid1 = nextReq->transId1;
const Uint32 transid2 = nextReq->transId2;
const Uint32 senderData = nextReq->senderData;
+ Uint32 hashHi = signal->getSendersBlockRef();
- if (findTransaction(transid1, transid2, senderData) != ZOK){
+ if (findTransaction(transid1, transid2, senderData, hashHi) != ZOK){
jam();
DEBUG(senderData <<
" Received SCAN_NEXTREQ in LQH with close flag when closed");
@@ -9492,15 +9528,16 @@ void Dblqh::execSCAN_NEXTREQ(Signal* sig
return;
}
}//if
- if (ERROR_INSERTED(5025)){
- // Delay signal if sender is NOT same node
- if (refToNode(signal->senderBlockRef()) != cownNodeid) {
- CLEAR_ERROR_INSERT_VALUE;
- sendSignalWithDelay(cownref, GSN_SCAN_NEXTREQ, signal, 1000,
- signal->length());
- return;
- }
- }//if
+ if (ERROR_INSERTED(5025))
+ {
+ /**
+ * This does not work as signal->getSendersBlockRef() is used
+ * as "hashHi"...not having a real data-word for this is not optimal
+ * but it will work...summary: disable this ERROR_INSERT
+ */
+ CLEAR_ERROR_INSERT_VALUE;
+ }
+
if (ERROR_INSERTED(5030)){
ndbout << "ERROR 5030" << endl;
CLEAR_ERROR_INSERT_VALUE;
@@ -9512,6 +9549,15 @@ void Dblqh::execSCAN_NEXTREQ(Signal* sig
return;
}
+ Uint32 pos = 0;
+ if (ScanFragNextReq::getCorrFactorFlag(nextReq->requestInfo))
+ {
+ jam();
+ Uint32 corrFactorLo = nextReq->variableData[pos++];
+ tcConnectptr.p->m_corrFactorLo &= 0xFFFF0000;
+ tcConnectptr.p->m_corrFactorLo |= corrFactorLo;
+ }
+
scanptr.i = tcConnectptr.p->tcScanRec;
ndbrequire(scanptr.i != RNIL);
c_scanRecordPool.getPtr(scanptr);
@@ -9954,6 +10000,7 @@ void Dblqh::execSCAN_FRAGREQ(Signal* sig
Uint32 senderData;
Uint32 hashIndex;
TcConnectionrecPtr nextHashptr;
+ Uint32 senderHi = signal->getSendersBlockRef();
const Uint32 reqinfo = scanFragReq->requestInfo;
@@ -10004,7 +10051,7 @@ void Dblqh::execSCAN_FRAGREQ(Signal* sig
goto error_handler_early_1;
}
- if (cfirstfreeTcConrec != RNIL) {
+ if (cfirstfreeTcConrec != RNIL && !ERROR_INSERTED_CLEAR(5055)) {
seizeTcrec();
tcConnectptr.p->clientConnectrec = scanFragReq->senderData;
tcConnectptr.p->clientBlockref = signal->senderBlockRef();
@@ -10059,7 +10106,8 @@ void Dblqh::execSCAN_FRAGREQ(Signal* sig
transid1,
transid2,
fragId,
- ZNIL);
+ ZNIL,
+ senderHi);
tcConnectptr.p->save1 = 0;
tcConnectptr.p->primKeyLen = keyLen;
tcConnectptr.p->applRef = scanFragReq->resultRef;
@@ -10074,6 +10122,15 @@ void Dblqh::execSCAN_FRAGREQ(Signal* sig
handle.clear();
}
+ if (ScanFragReq::getCorrFactorFlag(reqinfo))
+ {
+ /**
+ * Correlattion factor for SPJ
+ */
+ tcConnectptr.p->m_corrFactorLo = scanFragReq->variableData[0];
+ tcConnectptr.p->m_corrFactorHi = scanFragReq->variableData[1];
+ }
+
errorCode = initScanrec(scanFragReq, aiLen);
if (errorCode != ZOK) {
jam();
@@ -10410,8 +10467,10 @@ Dblqh::copyNextRange(Uint32 * dst, TcCon
Uint32 firstWord;
ndbrequire( keyInfoReader.getWord(&firstWord) );
const Uint32 rangeLen= (firstWord >> 16) ? (firstWord >> 16) : totalLen;
- tcPtrP->m_scan_curr_range_no= (firstWord & 0xFFF0) >> 4;
-
+ Uint32 range_no = (firstWord & 0xFFF0) >> 4;
+ tcPtrP->m_scan_curr_range_no= range_no;
+ tcPtrP->m_corrFactorLo &= 0x0000FFFF;
+ tcPtrP->m_corrFactorLo |= (range_no << 16);
firstWord &= 0xF; // Remove length+range num from first word
/* Write range info to dst */
@@ -11344,7 +11403,8 @@ void Dblqh::initScanTc(const ScanFragReq
Uint32 transid1,
Uint32 transid2,
Uint32 fragId,
- Uint32 nodeId)
+ Uint32 nodeId,
+ Uint32 hashHi)
{
tcConnectptr.p->transid[0] = transid1;
tcConnectptr.p->transid[1] = transid2;
@@ -11353,6 +11413,7 @@ void Dblqh::initScanTc(const ScanFragReq
tcConnectptr.p->fragmentid = fragId;
tcConnectptr.p->fragmentptr = fragptr.i;
tcConnectptr.p->tcOprec = tcConnectptr.p->clientConnectrec;
+ tcConnectptr.p->tcHashKeyHi = hashHi;
tcConnectptr.p->tcBlockref = tcConnectptr.p->clientBlockref;
tcConnectptr.p->errorCode = 0;
tcConnectptr.p->reclenAiLqhkey = 0;
@@ -11907,12 +11968,14 @@ void Dblqh::execCOPY_FRAGREQ(Signal* sig
0,
(DBLQH << 20) + (cownNodeid << 8),
fragId,
- copyFragReq->nodeId);
+ copyFragReq->nodeId,
+ 0);
cactiveCopy[cnoActiveCopy] = fragptr.i;
cnoActiveCopy++;
tcConnectptr.p->copyCountWords = 0;
tcConnectptr.p->tcOprec = tcConnectptr.i;
+ tcConnectptr.p->tcHashKeyHi = 0;
tcConnectptr.p->schemaVersion = scanptr.p->scanSchemaVersion;
tcConnectptr.p->savePointId = gci;
tcConnectptr.p->applRef = 0;
@@ -18116,6 +18179,7 @@ void Dblqh::execLogRecord(Signal* signal
tcConnectptr.p->nextReplica = refToNode(ref);
tcConnectptr.p->connectState = TcConnectionrec::LOG_CONNECTED;
tcConnectptr.p->tcOprec = tcConnectptr.i;
+ tcConnectptr.p->tcHashKeyHi = 0;
packLqhkeyreqLab(signal);
return;
}//Dblqh::execLogRecord()
=== added directory 'storage/ndb/src/kernel/blocks/dbspj'
=== added file 'storage/ndb/src/kernel/blocks/dbspj/Dbspj.hpp'
--- a/storage/ndb/src/kernel/blocks/dbspj/Dbspj.hpp 1970-01-01 00:00:00 +0000
+++ b/storage/ndb/src/kernel/blocks/dbspj/Dbspj.hpp 2011-02-23 19:28:26 +0000
@@ -0,0 +1,1168 @@
+/*
+ Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef DBSPJ_H
+#define DBSPJ_H
+
+#include <SimulatedBlock.hpp>
+#include <signaldata/LqhKey.hpp>
+#include <signaldata/ScanFrag.hpp>
+#include <AttributeHeader.hpp>
+#include <SLFifoList.hpp>
+#include <DLFifoList.hpp>
+#include <SLList.hpp>
+#include <ArenaPool.hpp>
+#include <DataBuffer2.hpp>
+#include <signaldata/DbspjErr.hpp>
+#include "../dbtup/tuppage.hpp"
+
+class SectionReader;
+struct QueryNode;
+struct QueryNodeParameters;
+
+//#define SPJ_TRACE_TIME
+
+#ifdef SPJ_TRACE_TIME
+static
+inline
+Uint64 spj_now()
+{
+ NDB_TICKS sec;
+ Uint32 micro;
+ NdbTick_CurrentMicrosecond(&sec, µ);
+ return Uint64(sec * 1000000 + micro);
+}
+#endif
+
+class Dbspj: public SimulatedBlock {
+public:
+ Dbspj(Block_context& ctx, Uint32 instanceNumber = 0);
+ virtual ~Dbspj();
+
+private:
+ BLOCK_DEFINES(Dbspj);
+
+ /**
+ * Signals from TC
+ */
+ void execLQHKEYREQ(Signal* signal);
+ void execSCAN_FRAGREQ(Signal* signal);
+ void execSCAN_NEXTREQ(Signal* signal);
+
+ void execDIH_SCAN_TAB_REF(Signal*);
+ void execDIH_SCAN_TAB_CONF(Signal*);
+ void execDIH_SCAN_GET_NODES_REF(Signal*);
+ void execDIH_SCAN_GET_NODES_CONF(Signal*);
+
+ /**
+ * Signals from LQH
+ */
+ void execLQHKEYREF(Signal* signal);
+ void execLQHKEYCONF(Signal* signal);
+ void execSCAN_FRAGREF(Signal* signal);
+ void execSCAN_FRAGCONF(Signal* signal);
+ void execSCAN_HBREP(Signal* signal);
+ void execTRANSID_AI(Signal*signal);
+
+ /**
+ * General signals
+ */
+ void execDUMP_STATE_ORD(Signal* signal){}
+ void execREAD_NODESCONF(Signal*);
+ void execREAD_CONFIG_REQ(Signal* signal);
+ void execSTTOR(Signal* signal);
+ void execDBINFO_SCANREQ(Signal* signal);
+ void execCONTINUEB(Signal*);
+ void execNODE_FAILREP(Signal*);
+ void execINCL_NODEREQ(Signal*);
+ void execAPI_FAILREQ(Signal*);
+
+ void sendSTTORRY(Signal* signal);
+
+protected:
+ //virtual bool getParam(const char* name, Uint32* count);
+
+public:
+ struct Request;
+ struct TreeNode;
+ struct ScanFragHandle;
+ typedef DataBuffer2<14, LocalArenaPoolImpl> Dependency_map;
+ typedef LocalDataBuffer2<14, LocalArenaPoolImpl> Local_dependency_map;
+ typedef DataBuffer2<14, LocalArenaPoolImpl> PatternStore;
+ typedef LocalDataBuffer2<14, LocalArenaPoolImpl> Local_pattern_store;
+
+ struct RowRef
+ {
+ Uint32 m_page_id;
+ Uint16 m_page_pos;
+ union
+ {
+ Uint16 unused;
+ Uint16 m_allocator;
+ };
+
+ void copyto_link(Uint32 * dst) const {
+ dst[0] = m_page_id; dst[1] = m_page_pos;
+ }
+ void assign_from_link(const Uint32 * src) {
+ m_page_id = src[0];
+ m_page_pos = src[1];
+ }
+
+ void copyto_map(Uint16 * dst) const {
+ dst[0] = Uint16(m_page_id);
+ dst[1] = Uint16(m_page_id >> 16);
+ dst[2] = m_page_pos;
+ }
+
+ void assign_from_map(const Uint16 * src) {
+ m_page_id = src[0];
+ m_page_id += Uint32(src[1]) << 16;
+ m_page_pos = src[2];
+ }
+
+ static bool map_is_null(const Uint16 * src) {
+ return src[2] == 0xFFFF;
+ }
+
+ void setNull() { m_page_id = RNIL;}
+ bool isNull() const { return m_page_id == RNIL;}
+ };
+
+ static const RowRef NullRowRef;
+
+ /**
+ * This struct represent a row being passed to a child
+ */
+ struct RowPtr
+ {
+ Uint32 m_type;
+ Uint32 m_src_node_no;
+ Uint32 m_src_node_ptrI;
+ Uint32 m_src_correlation;
+
+ struct Header
+ {
+ Uint32 m_len;
+ Uint32 m_offset[1];
+ };
+
+ struct Section
+ {
+ const Header * m_header;
+ SegmentedSectionPtrPOD m_dataPtr;
+ };
+
+ struct Linear
+ {
+ RowRef m_row_ref;
+ const Header * m_header;
+ const Uint32 * m_data;
+ };
+ union
+ {
+ struct Section m_section;
+ struct Linear m_linear;
+ } m_row_data;
+
+ enum RowType
+ {
+ RT_SECTION = 1,
+ RT_LINEAR = 2,
+ RT_END = 0
+ };
+ };
+
+ struct SLFifoRowList
+ {
+ /**
+ * Data used for a single linked list of rows
+ */
+ Uint32 m_first_row_page_id;
+ Uint32 m_last_row_page_id;
+ Uint16 m_first_row_page_pos;
+ Uint16 m_last_row_page_pos;
+
+ void init() { m_first_row_page_id = RNIL;}
+ bool isNull() const { return m_first_row_page_id == RNIL; }
+ };
+
+ struct RowMap
+ {
+ /**
+ * Data used for a map with rows (key is correlation id)
+ * currently a single array is used to store row references
+ * (size == batch size)
+ */
+ RowRef m_map_ref;
+ Uint16 m_size; // size of array
+ Uint16 m_elements; // #elements in array
+
+ void init() { m_map_ref.setNull();}
+ bool isNull() const { return m_map_ref.isNull(); }
+
+ void assign (RowRef ref) {
+ m_map_ref = ref;
+ }
+
+ void copyto(RowRef& ref) const {
+ ref = m_map_ref;
+ }
+
+ /**
+ * functions for manipulating *content* of map
+ */
+ void clear(Uint32 * ptr) {
+ memset(ptr, 0xFF, MAP_SIZE_PER_REF_16 * m_size * sizeof(Uint16));
+ }
+ void store(Uint32 * _ptr, Uint32 pos, RowRef ref) {
+ Uint16 * ptr = (Uint16*)_ptr;
+ ptr += MAP_SIZE_PER_REF_16 * pos;
+ ref.copyto_map(ptr);
+ m_elements++;
+ }
+ static void load(const Uint32 * _ptr, Uint32 pos, RowRef & ref) {
+ const Uint16 * ptr = (const Uint16*)_ptr;
+ ptr += MAP_SIZE_PER_REF_16 * pos;
+ ref.assign_from_map(ptr);
+ }
+ static bool isNull(const Uint32 * _ptr, Uint32 pos) {
+ const Uint16 * ptr = (const Uint16*)_ptr;
+ ptr += MAP_SIZE_PER_REF_16 * pos;
+ return RowRef::map_is_null(ptr);
+ }
+
+ STATIC_CONST( MAP_SIZE_PER_REF_16 = 3 );
+ };
+
+ struct SLFifoRowListIterator
+ {
+ RowRef m_ref;
+ Uint32 * m_row_ptr;
+
+ bool isNull() const { return m_ref.isNull(); }
+ void setNull() { m_ref.setNull(); }
+ };
+
+ struct SLFifoRowListIteratorPtr
+ {
+ RowRef m_ref;
+ };
+
+ struct RowMapIterator
+ {
+ Uint32 * m_row_ptr;
+ Uint32 * m_map_ptr;
+ RowRef m_ref; // position of actual row
+ Uint16 m_size;
+ Uint16 m_element_no;
+ bool isNull() const { return m_ref.isNull(); }
+ void setNull() { m_ref.setNull(); }
+ };
+
+ struct RowMapIteratorPtr
+ {
+ Uint32 m_element_no;
+ };
+
+
+ /**
+ * A struct used when building an TreeNode
+ */
+ struct Build_context
+ {
+ Uint32 m_cnt;
+ Uint32 m_scanPrio;
+ Uint32 m_savepointId;
+ Uint32 m_batch_size_rows;
+ Uint32 m_resultRef; // API
+ Uint32 m_resultData; // API
+ Uint32 m_senderRef; // TC (used for routing)
+ Uint32 m_scan_cnt;
+ Signal* m_start_signal; // Argument to first node in tree
+ SegmentedSectionPtr m_keyPtr;
+
+ // Used for resolving dependencies
+ Ptr<TreeNode> m_node_list[NDB_SPJ_MAX_TREE_NODES];
+ };
+
+ struct RowPage
+ {
+ /**
+ * NOTE: This contains various padding to be binary aligned with Tup_page
+ * (for storing into DLFifoList<RowPage>
+ */
+ RowPage() {}
+ struct File_formats::Page_header m_page_header;
+ Uint32 unused0;
+ Uint32 unused1;
+ Uint32 nextList;
+ Uint32 prevList;
+ Uint32 m_data[GLOBAL_PAGE_SIZE_WORDS - 7];
+ STATIC_CONST( SIZE = GLOBAL_PAGE_SIZE_WORDS - 7 );
+ };
+
+ typedef Tup_varsize_page Var_page;
+
+ struct RowBuffer
+ {
+ RowBuffer() { stack_init(); }
+ DLFifoList<RowPage>::Head m_page_list;
+
+ void stack_init() { new (&m_page_list) DLFifoList<RowPage>::Head(); m_stack.m_pos = 0xFFFF; }
+ void var_init() { new (&m_page_list) DLFifoList<RowPage>::Head(); m_var.m_free = 0; }
+
+ struct Stack
+ {
+ Uint32 m_pos; // position on head-page
+ };
+
+ struct Var
+ {
+ Uint32 m_free; // Free on last page in list
+ };
+
+ union {
+ struct Stack m_stack;
+ struct Var m_var;
+ };
+ };
+
+ /**
+ * A struct for building DA-part
+ * that is shared between QN_LookupNode & QN_ScanFragNode
+ */
+ struct DABuffer
+ {
+ const Uint32 * ptr;
+ const Uint32 * end;
+ };
+
+ /**
+ * A struct with "virtual" functions for different operations
+ */
+ struct OpInfo
+ {
+ /**
+ * This function create a operation suitable
+ * for execution
+ */
+ Uint32 (Dbspj::*m_build)(Build_context&ctx, Ptr<Request>,
+ const QueryNode*, const QueryNodeParameters*);
+
+ /**
+ * This function is called after build, but before start
+ * it's allowed to block (i.e send signals)
+ * and should if so increase request::m_outstanding
+ */
+ void (Dbspj::*m_prepare)(Signal*, Ptr<Request>, Ptr<TreeNode>);
+
+ /**
+ * This function is used for starting a request
+ */
+ void (Dbspj::*m_start)(Signal*, Ptr<Request>, Ptr<TreeNode>);
+
+ /**
+ * This function is used when getting a TRANSID_AI
+ */
+ void (Dbspj::*m_execTRANSID_AI)(Signal*,Ptr<Request>,Ptr<TreeNode>,
+ const RowPtr&);
+
+ /**
+ * This function is used when getting a LQHKEYREF
+ */
+ void (Dbspj::*m_execLQHKEYREF)(Signal*, Ptr<Request>, Ptr<TreeNode>);
+
+ /**
+ * This function is used when getting a LQHKEYCONF
+ */
+ void (Dbspj::*m_execLQHKEYCONF)(Signal*, Ptr<Request>, Ptr<TreeNode>);
+
+ /**
+ * This function is used when getting a SCAN_FRAGREF
+ */
+ void (Dbspj::*m_execSCAN_FRAGREF)(Signal*, Ptr<Request>, Ptr<TreeNode>, Ptr<ScanFragHandle>);
+
+ /**
+ * This function is used when getting a SCAN_FRAGCONF
+ */
+ void (Dbspj::*m_execSCAN_FRAGCONF)(Signal*, Ptr<Request>, Ptr<TreeNode>, Ptr<ScanFragHandle>);
+
+ /**
+ * This function is called on the *child* by the *parent* when passing rows
+ */
+ void (Dbspj::*m_parent_row)(Signal*,Ptr<Request>,Ptr<TreeNode>,
+ const RowPtr&);
+
+ /**
+ * This function is called on the *child* by the *parent* when *parent*
+ * has completed a batch
+ */
+ void (Dbspj::*m_parent_batch_complete)(Signal*,Ptr<Request>,Ptr<TreeNode>);
+
+ /**
+ * This function is called when getting a SCAN_NEXTREQ
+ */
+ void (Dbspj::*m_execSCAN_NEXTREQ)(Signal*, Ptr<Request>,Ptr<TreeNode>);
+
+ /**
+ * This function is called when all nodes in tree are finished
+ * it's allowed to "block" (by increaseing requestPtr.p->m_outstanding)
+ */
+ void (Dbspj::*m_complete)(Signal*, Ptr<Request>,Ptr<TreeNode>);
+
+ /**
+ * This function is called when a tree is aborted
+ * it's allowed to "block" (by increaseing requestPtr.p->m_outstanding)
+ */
+ void (Dbspj::*m_abort)(Signal*, Ptr<Request>, Ptr<TreeNode>);
+
+ /**
+ * This function is called on node-failure
+ */
+ Uint32 (Dbspj::*m_execNODE_FAILREP)(Signal*, Ptr<Request>, Ptr<TreeNode>,
+ NdbNodeBitmask);
+ /**
+ * This function is called when request/node(s) is/are removed
+ * should only do local cleanup(s)
+ */
+ void (Dbspj::*m_cleanup)(Ptr<Request>, Ptr<TreeNode>);
+ };
+
+ struct LookupData
+ {
+ Uint32 m_api_resultRef;
+ Uint32 m_api_resultData;
+ /**
+ * This is the number of outstanding messages. When this number is zero
+ * and m_parent_batch_complete is true, we know that we have received
+ * all rows for this operation in this batch.
+ */
+ Uint32 m_outstanding;
+ /**
+ * If true, the parent operation has received all the rows it will get
+ * in this batch.
+ */
+ bool m_parent_batch_complete;
+ Uint32 m_lqhKeyReq[LqhKeyReq::FixedSignalLength + 4];
+ };
+
+ struct ScanFragData
+ {
+ Uint32 m_rows_received; // #execTRANSID_AI
+ Uint32 m_rows_expecting; // ScanFragConf
+ Uint32 m_scanFragReq[ScanFragReq::SignalLength + 2];
+ Uint32 m_scanFragHandlePtrI;
+ };
+
+ struct ScanFragHandle
+ {
+ enum SFH_State
+ {
+ SFH_NOT_STARTED = 0,
+ SFH_SCANNING = 1, // in LQH
+ SFH_WAIT_NEXTREQ = 2,
+ SFH_COMPLETE = 3,
+ SFH_WAIT_CLOSE = 4
+ };
+
+ void init(Uint32 fid) {
+ m_ref = 0;
+ m_fragId = fid;
+ m_state = SFH_NOT_STARTED;
+ m_rangePtrI = RNIL;
+ reset_ranges();
+ }
+
+ Uint32 m_magic;
+ Uint32 m_treeNodePtrI;
+ Uint16 m_fragId;
+ Uint16 m_state;
+ Uint32 m_ref;
+
+ void reset_ranges() {
+ // m_rangePtrI is explicitly managed...in code
+ m_range_builder.m_range_cnt = m_range_builder.m_range_size = 0;
+ }
+ struct RangeBuilder
+ {
+ Uint16 m_range_size;
+ Uint16 m_range_cnt; // too set bounds info correctly
+ } m_range_builder;
+ Uint32 m_rangePtrI;
+ union {
+ Uint32 nextList;
+ Uint32 nextPool;
+ };
+ };
+
+ typedef RecordPool<ScanFragHandle, ArenaPool> ScanFragHandle_pool;
+ typedef SLFifoListImpl<ScanFragHandle_pool, ScanFragHandle> ScanFragHandle_list;
+ typedef LocalSLFifoListImpl<ScanFragHandle_pool, ScanFragHandle> Local_ScanFragHandle_list;
+
+ struct ScanIndexData
+ {
+ Uint16 m_frags_not_complete;
+ Uint16 m_frags_outstanding;
+ Uint32 m_rows_received; // #execTRANSID_AI
+ Uint32 m_rows_expecting; // Sum(ScanFragConf)
+ Uint32 m_scanCookie;
+ Uint32 m_fragCount;
+ ScanFragHandle_list::HeadPOD m_fragments; // ScanFrag states
+ union
+ {
+ PatternStore::HeadPOD m_prunePattern;
+ Uint32 m_constPrunePtrI;
+ };
+ Uint32 m_scanFragReq[ScanFragReq::SignalLength + 2];
+ };
+
+ struct TreeNode_cursor_ptr
+ {
+ Uint32 nextList;
+ };
+
+ /**
+ * A node in a Query
+ * (This is an instantiated version of QueryNode in
+ * include/kernel/signal/QueryTree.hpp)
+ */
+ struct TreeNode : TreeNode_cursor_ptr
+ {
+ STATIC_CONST ( MAGIC = ~RT_SPJ_TREENODE );
+
+ TreeNode()
+ : m_magic(MAGIC), m_state(TN_END),
+ m_parentPtrI(RNIL), m_requestPtrI(0)
+ {
+ }
+
+ TreeNode(Uint32 request)
+ : m_magic(MAGIC),
+ m_info(0), m_bits(T_LEAF), m_state(TN_BUILDING),
+ m_parentPtrI(RNIL), m_requestPtrI(request),
+ nextList(RNIL), prevList(RNIL)
+ {
+// m_send.m_ref = 0;
+ m_send.m_correlation = 0;
+ m_send.m_keyInfoPtrI = RNIL;
+ m_send.m_attrInfoPtrI = RNIL;
+ }
+
+ const Uint32 m_magic;
+ const struct OpInfo* m_info;
+
+ enum TreeNodeState
+ {
+ /**
+ * Initial
+ */
+ TN_BUILDING = 1,
+
+ /**
+ * Tree node is preparing
+ */
+ TN_PREPARING = 2,
+
+ /**
+ * Tree node is build and prepared, but not active
+ */
+ TN_INACTIVE = 3,
+
+ /**
+ * Tree node is active (i.e has outstanding request(s))
+ */
+ TN_ACTIVE = 4,
+
+ /**
+ * Tree node is "finishing" (after TN_INACTIVE)
+ */
+ TN_COMPLETING = 5,
+
+ /**
+ * end-marker, not a valid state
+ */
+ TN_END = 0
+ };
+
+ enum TreeNodeBits
+ {
+ T_ATTR_INTERPRETED = 0x1,
+
+ /**
+ * Will node be executed only once (::parent_row())
+ * implies key/attr-info will be disowned (by send-signal)
+ */
+ T_ONE_SHOT = 0x2,
+
+ /**
+ * Is keyinfo "constructed"
+ * (implies key info will be disowned (by send-signal)
+ */
+ T_KEYINFO_CONSTRUCTED = 0x4,
+
+ /**
+ * Is attrinfo "constructed"
+ * (implies attr info will be disowned (by send-signal)
+ */
+ T_ATTRINFO_CONSTRUCTED = 0x8,
+
+ /**
+ * Is this node a leaf-node
+ */
+ T_LEAF = 0x10,
+
+ /**
+ * Does this node have a user projection. (The index access part of
+ * an index lookup operation has no user projection, since only the
+ * base table tuple is sent to the API.)
+ */
+ T_USER_PROJECTION = 0x20,
+
+ /**
+ * Is this a unique index lookup (on index table)
+ * (implies some extra error handling code)
+ */
+ T_UNIQUE_INDEX_LOOKUP = 0x40,
+
+ /*
+ * Should this node buffers its rows
+ */
+ T_ROW_BUFFER = 0x80,
+
+ /**
+ * Should rows have dictionary (i.e random access capability)
+ * This is typically used when having nodes depending on multiple parents
+ * so that when row gets availble from "last" parent, a key can be
+ * constructed using correlation value from parents
+ */
+ T_ROW_BUFFER_MAP = 0x100,
+
+ /**
+ * Does any child need to know about when *my* batch is complete
+ */
+ T_REPORT_BATCH_COMPLETE = 0x200,
+
+ /**
+ * Do I need to know when parent batch is cimpleted
+ */
+ T_NEED_REPORT_BATCH_COMPLETED = 0x400,
+
+ /**
+ * Constant prune pattern
+ */
+ T_CONST_PRUNE = 0x800,
+
+ /**
+ * Prune pattern
+ */
+ T_PRUNE_PATTERN = 0x1000,
+
+ /**
+ * Should index scan be parallel
+ */
+ T_SCAN_PARALLEL = 0x2000,
+
+ // End marker...
+ T_END = 0
+ };
+
+ bool isLeaf() const { return (m_bits & T_LEAF) != 0;}
+
+ Uint32 m_bits;
+ Uint32 m_state;
+ Uint32 m_node_no;
+ Uint32 m_batch_size;
+ Uint32 m_parentPtrI;
+ const Uint32 m_requestPtrI;
+ Dependency_map::Head m_dependent_nodes;
+ PatternStore::Head m_keyPattern;
+ PatternStore::Head m_attrParamPattern;
+
+ /**
+ * Rows buffered by this node
+ */
+ union
+ {
+ RowMap m_row_map;
+ SLFifoRowList m_row_list;
+ };
+
+ union
+ {
+ LookupData m_lookup_data;
+ ScanFragData m_scanfrag_data;
+ ScanIndexData m_scanindex_data;
+ };
+
+ struct {
+ Uint32 m_ref; // dst for signal
+ /** Each tuple has a 16-bit id that is unique within that operation,
+ * batch and SPJ block instance. The upper half word of m_correlation
+ * is the id of the parent tuple, and the lower half word is the
+ * id of the current tuple.*/
+ Uint32 m_correlation;
+ Uint32 m_keyInfoPtrI; // keyInfoSection
+ Uint32 m_attrInfoPtrI; // attrInfoSection
+ } m_send;
+
+ union {
+ Uint32 nextList;
+ Uint32 nextPool;
+ };
+ Uint32 prevList;
+ };
+
+ static const Ptr<TreeNode> NullTreeNodePtr;
+
+ typedef RecordPool<TreeNode, ArenaPool> TreeNode_pool;
+ typedef DLFifoListImpl<TreeNode_pool, TreeNode> TreeNode_list;
+ typedef LocalDLFifoListImpl<TreeNode_pool, TreeNode> Local_TreeNode_list;
+
+ typedef SLListImpl<TreeNode_pool, TreeNode, TreeNode_cursor_ptr>
+ TreeNodeCursor_list;
+ typedef LocalSLListImpl<TreeNode_pool, TreeNode, TreeNode_cursor_ptr>
+ Local_TreeNodeCursor_list;
+
+ /**
+ * A request (i.e a query + parameters)
+ */
+ struct Request
+ {
+ enum RequestBits
+ {
+ RT_SCAN = 0x1 // unbounded result set, scan interface
+ ,RT_ROW_BUFFERS = 0x2 // Do any of the node use row-buffering
+ ,RT_MULTI_SCAN = 0x4 // Is there several scans in request
+ ,RT_VAR_ALLOC = 0x8 // Is var-allocation used for row-buffer
+ ,RT_NEED_PREPARE = 0x10 // Does any node need m_prepare hook
+ ,RT_NEED_COMPLETE = 0x20 // Does any node need m_complete hook
+ };
+
+ enum RequestState
+ {
+ RS_BUILDING = 0x1,
+ RS_PREPARING = 0x2,
+ RS_RUNNING = 0x3,
+ RS_COMPLETING = 0x4,
+
+ RS_ABORTING = 0x1000, // Or:ed together with other states
+ RS_WAITING = 0x2000, // Waiting for SCAN_NEXTREQ
+
+ RS_ABORTED = 0x2008, // Aborted and waiting for SCAN_NEXTREQ
+ RS_END = 0
+ };
+
+ Request() {}
+ Request(const ArenaHead & arena) : m_arena(arena) {}
+ Uint32 m_magic;
+ Uint32 m_bits;
+ Uint32 m_state;
+ Uint32 m_errCode;
+ Uint32 m_node_cnt;
+ Uint32 m_senderRef;
+ Uint32 m_senderData;
+ Uint32 m_rootResultData;
+ Uint32 m_transId[2];
+ TreeNode_list::Head m_nodes;
+ TreeNodeCursor_list::Head m_cursor_nodes;
+ Uint32 m_cnt_active; // No of "running" nodes
+ Bitmask<1> m_active_nodes; // Nodes which will return more data
+ Uint32 m_rows; // Rows accumulated in current batch
+ Uint32 m_outstanding; // Outstanding signals, when 0, batch is done
+ Uint16 m_lookup_node_data[MAX_NDB_NODES];
+ ArenaHead m_arena;
+ RowBuffer m_rowBuffer;
+
+#ifdef SPJ_TRACE_TIME
+ Uint32 m_cnt_batches;
+ Uint32 m_sum_rows;
+ Uint32 m_sum_running;
+ Uint32 m_sum_waiting;
+ Uint64 m_save_time;
+#endif
+
+ bool isScan() const { return (m_bits & RT_SCAN) != 0;}
+ bool isLookup() const { return (m_bits & RT_SCAN) == 0;}
+
+ bool equal(const Request & key) const {
+ return
+ m_senderData == key.m_senderData &&
+ m_transId[0] == key.m_transId[0] &&
+ m_transId[1] == key.m_transId[1];
+ }
+
+ Uint32 hashValue() const {
+ return m_transId[0] ^ m_senderData;
+ }
+
+ union {
+ Uint32 nextHash;
+ Uint32 nextPool;
+ };
+ Uint32 prevHash;
+ };
+
+private:
+ /**
+ * These are the rows in ndbinfo.counters that concerns the SPJ block.
+ * @see Ndbinfo::counter_id.
+ */
+ enum CounterId
+ {
+ /**
+ * This is the number of incomming LQHKEYREQ messages (i.e queries with a
+ * lookup as root).
+ */
+ CI_READS_RECEIVED = 0,
+
+ /**
+ * This is the number of lookup operations (LQHKEYREQ) sent to a local
+ * LQH block.
+ */
+ CI_LOCAL_READS_SENT = 1,
+
+ /**
+ * This is the number of lookup operations (LQHKEYREQ) sent to a remote
+ * LQH block.
+ */
+ CI_REMOTE_READS_SENT = 2,
+
+ /**
+ * No of lookup operations which did not return a row (LQHKEYREF).
+ * (Most likely due to non matching key, or predicate
+ * filter which evalueted to 'false').
+ */
+ CI_READS_NOT_FOUND = 3,
+
+ /**
+ * This is the number of incomming queries where the root operation is a
+ * fragment scan and this is a "direct scan" that does not go via an index.
+ */
+ CI_TABLE_SCANS_RECEIVED = 4,
+
+ /**
+ * This is the number of "direct" fragment scans (i.e. no via an ordered
+ * index)sent to the local LQH block.
+ */
+ CI_LOCAL_TABLE_SCANS_SENT = 5,
+
+ /**
+ * This is the number of incomming queries where the root operation is a
+ * fragment scan which scans the fragment via an ordered index..
+ */
+ CI_RANGE_SCANS_RECEIVED = 6,
+
+ /**
+ * This the number of scans using ordered indexes that have been sent to the
+ * local LQH block.
+ */
+ CI_LOCAL_RANGE_SCANS_SENT = 7,
+
+ /**
+ * This the number of scans using ordered indexes that have been sent to a
+ * remote LQH block.
+ */
+ CI_REMOTE_RANGE_SCANS_SENT = 8,
+
+ /**
+ * No of scan batches (on range or full table) returned to ndbapi
+ */
+ CI_SCAN_BATCHES_RETURNED = 9,
+
+ /**
+ * Total no of rows returned from scans.
+ */
+ CI_SCAN_ROWS_RETURNED = 10,
+
+ /**
+ * No of prunable indexscans that has been received
+ */
+ CI_PRUNED_RANGE_SCANS_RECEIVED = 11,
+
+ /**
+ * No of "const" prunable index scans that has been received
+ * i.e index-scan only access 1 partition
+ */
+ CI_CONST_PRUNED_RANGE_SCANS_RECEIVED = 12,
+
+ CI_END = 13 // End marker - not a valid counter id.
+ };
+
+ /**
+ * This is a set of counters for monitoring the behavior of the SPJ block.
+ * They may be read through the ndbinfo.counters SQL table.
+ */
+ class MonotonicCounters {
+ public:
+
+ MonotonicCounters()
+ {
+ for(int i = 0; i < CI_END; i++)
+ {
+ m_counters[i] = 0;
+ }
+ }
+
+ Uint64 get_counter(CounterId id) const
+ {
+ return m_counters[id];
+ }
+
+ void incr_counter(CounterId id, Uint64 delta)
+ {
+ m_counters[id] += delta;
+ }
+
+ private:
+ Uint64 m_counters[CI_END];
+
+ } c_Counters;
+
+ typedef RecordPool<Request, ArenaPool> Request_pool;
+ typedef DLListImpl<Request_pool, Request> Request_list;
+ typedef LocalDLListImpl<Request_pool, Request> Local_Request_list;
+ typedef DLHashTableImpl<Request_pool, Request> Request_hash;
+ typedef DLHashTableImpl<Request_pool, Request>::Iterator Request_iterator;
+
+ ArenaAllocator m_arenaAllocator;
+ Request_pool m_request_pool;
+ Request_hash m_scan_request_hash;
+ Request_hash m_lookup_request_hash;
+ ArenaPool m_dependency_map_pool;
+ TreeNode_pool m_treenode_pool;
+ ScanFragHandle_pool m_scanfraghandle_pool;
+
+ NdbNodeBitmask c_alive_nodes;
+
+ void do_init(Request*, const LqhKeyReq*, Uint32 senderRef);
+ void store_lookup(Ptr<Request>);
+ void handle_early_lqhkey_ref(Signal*, const LqhKeyReq *, Uint32 err);
+ void sendTCKEYREF(Signal* signal, Uint32 ref, Uint32 routeRef);
+ void sendTCKEYCONF(Signal* signal, Uint32 len, Uint32 ref, Uint32 routeRef);
+
+ void do_init(Request*, const ScanFragReq*, Uint32 senderRef);
+ void store_scan(Ptr<Request>);
+ void handle_early_scanfrag_ref(Signal*, const ScanFragReq *, Uint32 err);
+
+ struct BuildKeyReq
+ {
+ Uint32 hashInfo[4]; // Used for hashing
+ Uint32 fragId;
+ Uint32 fragDistKey;
+ Uint32 receiverRef; // NodeId + InstanceNo
+ };
+
+ /**
+ * Build
+ */
+ const OpInfo* getOpInfo(Uint32 op);
+ Uint32 build(Build_context&,Ptr<Request>,SectionReader&,SectionReader&);
+ void checkPrepareComplete(Signal*, Ptr<Request>, Uint32 cnt);
+ void start(Signal*, Ptr<Request>);
+ void checkBatchComplete(Signal*, Ptr<Request>, Uint32 cnt);
+ void batchComplete(Signal*, Ptr<Request>);
+ void sendConf(Signal*, Ptr<Request>, bool is_complete);
+ void complete(Signal*, Ptr<Request>);
+ void cleanup(Ptr<Request>);
+ void abort(Signal*, Ptr<Request>, Uint32 errCode);
+ Uint32 nodeFail(Signal*, Ptr<Request>, NdbNodeBitmask mask);
+
+ Uint32 createNode(Build_context&, Ptr<Request>, Ptr<TreeNode> &);
+ void reportBatchComplete(Signal*, Ptr<Request>, Ptr<TreeNode>);
+ void releaseScanBuffers(Ptr<Request> requestPtr);
+ void releaseRequestBuffers(Ptr<Request> requestPtr, bool reset);
+ void releaseNodeRows(Ptr<Request> requestPtr, Ptr<TreeNode>);
+ void releaseRow(Ptr<Request>, RowRef ref);
+ Uint32 releaseScanBuffers(Ptr<Request> requestPtr, Ptr<TreeNode>);
+ void registerCursor(Ptr<Request>, Ptr<TreeNode>);
+ void nodeFail_checkRequests(Signal*);
+
+ void cleanup_common(Ptr<Request>, Ptr<TreeNode>);
+ void mark_active(Ptr<Request>, Ptr<TreeNode>, bool value);
+
+ /**
+ * Row buffering
+ */
+ Uint32 storeRow(Ptr<Request>, Ptr<TreeNode>, RowPtr &row);
+ Uint32* stackAlloc(RowBuffer& dst, RowRef&, Uint32 len);
+ Uint32* varAlloc(RowBuffer& dst, RowRef&, Uint32 len);
+
+ void add_to_list(SLFifoRowList & list, RowRef rowref);
+ Uint32 add_to_map(Ptr<Request> requestPtr, Ptr<TreeNode>, Uint32, RowRef);
+ Uint32 * get_row_ptr(const RowMap&, RowMapIterator pos);
+ void setupRowPtr(Ptr<TreeNode>, RowPtr& dst, RowRef, const Uint32 * src);
+
+ // NOTE: ref contains info about it being stack/var
+ // so adding an inline would be nice...but that remove possibility
+ // to add jam()'s
+ Uint32 * get_row_ptr_stack(RowRef pos);
+ Uint32 * get_row_ptr_var(RowRef pos);
+
+ /**
+ * SLFifoRowListIterator
+ */
+ bool first(Ptr<Request>, Ptr<TreeNode>, SLFifoRowListIterator&);
+ bool next(SLFifoRowListIterator&);
+ bool next(Ptr<Request>, Ptr<TreeNode>, SLFifoRowListIterator&, SLFifoRowListIteratorPtr);
+
+ bool first(Ptr<Request>, Ptr<TreeNode>, RowMapIterator&);
+ bool next(RowMapIterator&);
+ bool next(Ptr<Request>,Ptr<TreeNode>, RowMapIterator&, RowMapIteratorPtr);
+
+ /**
+ * Misc
+ */
+ Uint32 buildRowHeader(RowPtr::Header *, SegmentedSectionPtr);
+ Uint32 buildRowHeader(RowPtr::Header *, const Uint32 *& src, Uint32 len);
+ void getCorrelationData(const RowPtr::Section & row, Uint32 col,
+ Uint32& correlationNumber);
+ void getCorrelationData(const RowPtr::Linear & row, Uint32 col,
+ Uint32& correlationNumber);
+ Uint32 appendToPattern(Local_pattern_store &, DABuffer & tree, Uint32);
+ Uint32 appendParamToPattern(Local_pattern_store&,const RowPtr::Linear&,
+ Uint32);
+ Uint32 appendParamHeadToPattern(Local_pattern_store&,const RowPtr::Linear&,
+ Uint32);
+
+ Uint32 appendTreeToSection(Uint32 & ptrI, SectionReader &, Uint32);
+ Uint32 appendColToSection(Uint32 & ptrI, const RowPtr::Linear&, Uint32 col, bool& hasNull);
+ Uint32 appendColToSection(Uint32 & ptrI, const RowPtr::Section&, Uint32 col, bool& hasNull);
+ Uint32 appendPkColToSection(Uint32 & ptrI, const RowPtr::Section&,Uint32 col);
+ Uint32 appendPkColToSection(Uint32 & ptrI, const RowPtr::Linear&, Uint32 col);
+ Uint32 appendAttrinfoToSection(Uint32 &, const RowPtr::Linear&, Uint32 col, bool& hasNull);
+ Uint32 appendAttrinfoToSection(Uint32 &, const RowPtr::Section&, Uint32 col, bool& hasNull);
+ Uint32 appendDataToSection(Uint32 & ptrI, Local_pattern_store&,
+ Local_pattern_store::ConstDataBufferIterator&,
+ Uint32 len, bool& hasNull);
+ Uint32 appendFromParent(Uint32 & ptrI, Local_pattern_store&,
+ Local_pattern_store::ConstDataBufferIterator&,
+ Uint32 level, const RowPtr&, bool& hasNull);
+ Uint32 expand(Uint32 & ptrI, Local_pattern_store& p, const RowPtr& r, bool& hasNull){
+ switch(r.m_type){
+ case RowPtr::RT_SECTION:
+ return expandS(ptrI, p, r, hasNull);
+ case RowPtr::RT_LINEAR:
+ return expandL(ptrI, p, r, hasNull);
+ }
+ return DbspjErr::InternalError;
+ }
+ Uint32 expandS(Uint32 & ptrI, Local_pattern_store&, const RowPtr&, bool& hasNull);
+ Uint32 expandL(Uint32 & ptrI, Local_pattern_store&, const RowPtr&, bool& hasNull);
+ Uint32 expand(Uint32 & ptrI, DABuffer& pattern, Uint32 len,
+ DABuffer & param, Uint32 cnt, bool& hasNull);
+ Uint32 expand(Local_pattern_store& dst, Ptr<TreeNode> treeNodePtr,
+ DABuffer & pattern, Uint32 len,
+ DABuffer & param, Uint32 cnt);
+ Uint32 parseDA(Build_context&, Ptr<Request>, Ptr<TreeNode>,
+ DABuffer & tree, Uint32 treeBits,
+ DABuffer & param, Uint32 paramBits);
+
+ Uint32 createEmptySection(Uint32 & ptrI);
+
+ Uint32 getResultRef(Ptr<Request> requestPtr);
+
+ /**
+ * Lookup
+ */
+ static const OpInfo g_LookupOpInfo;
+ Uint32 lookup_build(Build_context&,Ptr<Request>,
+ const QueryNode*, const QueryNodeParameters*);
+ void lookup_start(Signal*, Ptr<Request>, Ptr<TreeNode>);
+ void lookup_send(Signal*, Ptr<Request>, Ptr<TreeNode>);
+ void lookup_execTRANSID_AI(Signal*, Ptr<Request>, Ptr<TreeNode>,
+ const RowPtr&);
+ void lookup_execLQHKEYREF(Signal*, Ptr<Request>, Ptr<TreeNode>);
+ void lookup_execLQHKEYCONF(Signal*, Ptr<Request>, Ptr<TreeNode>);
+ void lookup_parent_row(Signal*, Ptr<Request>, Ptr<TreeNode>, const RowPtr &);
+ void lookup_parent_batch_complete(Signal*, Ptr<Request>, Ptr<TreeNode>);
+ void lookup_abort(Signal*, Ptr<Request>, Ptr<TreeNode>);
+ Uint32 lookup_execNODE_FAILREP(Signal*signal, Ptr<Request>, Ptr<TreeNode>,
+ NdbNodeBitmask);
+ void lookup_cleanup(Ptr<Request>, Ptr<TreeNode>);
+
+ Uint32 handle_special_hash(Uint32 tableId, Uint32 dstHash[4],
+ const Uint64* src,
+ Uint32 srcLen, // Len in #32bit words
+ const struct KeyDescriptor* desc);
+
+ Uint32 computeHash(Signal*, BuildKeyReq&, Uint32 table, Uint32 keyInfoPtrI);
+ Uint32 computePartitionHash(Signal*, BuildKeyReq&, Uint32 table, Uint32 keyInfoPtrI);
+ Uint32 getNodes(Signal*, BuildKeyReq&, Uint32 tableId);
+
+ /**
+ * ScanFrag
+ */
+ static const OpInfo g_ScanFragOpInfo;
+ Uint32 scanFrag_build(Build_context&, Ptr<Request>,
+ const QueryNode*, const QueryNodeParameters*);
+ void scanFrag_start(Signal*, Ptr<Request>,Ptr<TreeNode>);
+ void scanFrag_send(Signal*, Ptr<Request>, Ptr<TreeNode>);
+ void scanFrag_execTRANSID_AI(Signal*, Ptr<Request>, Ptr<TreeNode>,
+ const RowPtr &);
+ void scanFrag_execSCAN_FRAGREF(Signal*, Ptr<Request>, Ptr<TreeNode>, Ptr<ScanFragHandle>);
+ void scanFrag_execSCAN_FRAGCONF(Signal*, Ptr<Request>, Ptr<TreeNode>, Ptr<ScanFragHandle>);
+ void scanFrag_execSCAN_NEXTREQ(Signal*, Ptr<Request>,Ptr<TreeNode>);
+ void scanFrag_abort(Signal*, Ptr<Request>, Ptr<TreeNode>);
+ void scanFrag_cleanup(Ptr<Request>, Ptr<TreeNode>);
+
+ /**
+ * ScanIndex
+ */
+ static const OpInfo g_ScanIndexOpInfo;
+ Uint32 scanIndex_build(Build_context&, Ptr<Request>,
+ const QueryNode*, const QueryNodeParameters*);
+ Uint32 parseScanIndex(Build_context&, Ptr<Request>, Ptr<TreeNode>,
+ DABuffer tree, Uint32 treeBits,
+ DABuffer param, Uint32 paramBits);
+ void scanIndex_prepare(Signal*, Ptr<Request>, Ptr<TreeNode>);
+ void scanIndex_execTRANSID_AI(Signal*, Ptr<Request>, Ptr<TreeNode>,
+ const RowPtr &);
+ void scanIndex_execSCAN_FRAGREF(Signal*, Ptr<Request>, Ptr<TreeNode>, Ptr<ScanFragHandle>);
+ void scanIndex_execSCAN_FRAGCONF(Signal*, Ptr<Request>, Ptr<TreeNode>, Ptr<ScanFragHandle>);
+ void scanIndex_parent_row(Signal*,Ptr<Request>,Ptr<TreeNode>, const RowPtr&);
+ void scanIndex_fixupBound(Ptr<ScanFragHandle> fragPtr, Uint32 ptrI, Uint32);
+ void scanIndex_send(Signal*,Ptr<Request>,Ptr<TreeNode>);
+ void scanIndex_batchComplete(Signal* signal);
+ Uint32 scanIndex_findFrag(Local_ScanFragHandle_list &, Ptr<ScanFragHandle>&,
+ Uint32 fragId);
+ void scanIndex_parent_batch_complete(Signal*, Ptr<Request>, Ptr<TreeNode>);
+ void scanIndex_execSCAN_NEXTREQ(Signal*, Ptr<Request>,Ptr<TreeNode>);
+ void scanIndex_complete(Signal*, Ptr<Request>, Ptr<TreeNode>);
+ void scanIndex_abort(Signal*, Ptr<Request>, Ptr<TreeNode>);
+ Uint32 scanIndex_execNODE_FAILREP(Signal*signal, Ptr<Request>, Ptr<TreeNode>,
+ NdbNodeBitmask);
+ void scanIndex_cleanup(Ptr<Request>, Ptr<TreeNode>);
+
+ /**
+ * Page manager
+ */
+ bool allocPage(Ptr<RowPage> &);
+ void releasePage(Ptr<RowPage>);
+ void releasePages(Uint32 first, Ptr<RowPage> last);
+ void releaseGlobal(Signal*);
+ SLList<RowPage>::Head m_free_page_list;
+ ArrayPool<RowPage> m_page_pool;
+
+ /**
+ * Scratch buffers...
+ */
+ Uint32 m_buffer0[8192]; // 32k
+ Uint32 m_buffer1[8192]; // 32k
+};
+
+#endif
=== added file 'storage/ndb/src/kernel/blocks/dbspj/DbspjInit.cpp'
--- a/storage/ndb/src/kernel/blocks/dbspj/DbspjInit.cpp 1970-01-01 00:00:00 +0000
+++ b/storage/ndb/src/kernel/blocks/dbspj/DbspjInit.cpp 2011-02-23 19:28:26 +0000
@@ -0,0 +1,78 @@
+/*
+ Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <pc.hpp>
+#define DBSPJ_C
+#include "Dbspj.hpp"
+#include <ndb_limits.h>
+
+#define DEBUG(x) { ndbout << "SPJ::" << x << endl; }
+
+
+Dbspj::Dbspj(Block_context& ctx, Uint32 instanceNumber):
+ SimulatedBlock(DBSPJ, ctx, instanceNumber),
+ m_scan_request_hash(m_request_pool),
+ m_lookup_request_hash(m_request_pool)
+{
+ BLOCK_CONSTRUCTOR(Dbspj);
+
+ addRecSignal(GSN_DUMP_STATE_ORD, &Dbspj::execDUMP_STATE_ORD);
+ addRecSignal(GSN_READ_NODESCONF, &Dbspj::execREAD_NODESCONF);
+ addRecSignal(GSN_READ_CONFIG_REQ, &Dbspj::execREAD_CONFIG_REQ);
+ addRecSignal(GSN_STTOR, &Dbspj::execSTTOR);
+ addRecSignal(GSN_DBINFO_SCANREQ, &Dbspj::execDBINFO_SCANREQ);
+ addRecSignal(GSN_CONTINUEB, &Dbspj::execCONTINUEB);
+ addRecSignal(GSN_NODE_FAILREP, &Dbspj::execNODE_FAILREP);
+ addRecSignal(GSN_INCL_NODEREQ, &Dbspj::execINCL_NODEREQ);
+ addRecSignal(GSN_API_FAILREQ, &Dbspj::execAPI_FAILREQ);
+
+ /**
+ * Signals from DIH
+ */
+ addRecSignal(GSN_DIH_SCAN_TAB_REF, &Dbspj::execDIH_SCAN_TAB_REF);
+ addRecSignal(GSN_DIH_SCAN_TAB_CONF, &Dbspj::execDIH_SCAN_TAB_CONF);
+ addRecSignal(GSN_DIH_SCAN_GET_NODES_REF, &Dbspj::execDIH_SCAN_GET_NODES_REF);
+ addRecSignal(GSN_DIH_SCAN_GET_NODES_CONF,&Dbspj::execDIH_SCAN_GET_NODES_CONF);
+
+ /**
+ * Signals from TC
+ */
+ addRecSignal(GSN_LQHKEYREQ, &Dbspj::execLQHKEYREQ);
+ addRecSignal(GSN_SCAN_FRAGREQ, &Dbspj::execSCAN_FRAGREQ);
+ addRecSignal(GSN_SCAN_NEXTREQ, &Dbspj::execSCAN_NEXTREQ);
+
+ /**
+ * Signals from LQH
+ */
+ addRecSignal(GSN_LQHKEYREF, &Dbspj::execLQHKEYREF);
+ addRecSignal(GSN_LQHKEYCONF, &Dbspj::execLQHKEYCONF);
+ addRecSignal(GSN_SCAN_FRAGREF, &Dbspj::execSCAN_FRAGREF);
+ addRecSignal(GSN_SCAN_FRAGCONF, &Dbspj::execSCAN_FRAGCONF);
+ addRecSignal(GSN_TRANSID_AI, &Dbspj::execTRANSID_AI);
+ addRecSignal(GSN_SCAN_HBREP, &Dbspj::execSCAN_HBREP);
+
+ ndbout << "Instantiating DBSPJ instanceNo=" << instanceNumber << endl;
+}//Dbspj::Dbspj()
+
+Dbspj::~Dbspj()
+{
+ m_page_pool.clear();
+}//Dbspj::~Dbspj()
+
+
+BLOCK_FUNCTIONS(Dbspj)
+
=== added file 'storage/ndb/src/kernel/blocks/dbspj/DbspjMain.cpp'
--- a/storage/ndb/src/kernel/blocks/dbspj/DbspjMain.cpp 1970-01-01 00:00:00 +0000
+++ b/storage/ndb/src/kernel/blocks/dbspj/DbspjMain.cpp 2011-02-23 19:28:26 +0000
@@ -0,0 +1,6867 @@
+/*
+ Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#define DBSPJ_C
+#include "Dbspj.hpp"
+
+#include <SectionReader.hpp>
+#include <signaldata/LqhKey.hpp>
+#include <signaldata/QueryTree.hpp>
+#include <signaldata/TcKeyRef.hpp>
+#include <signaldata/RouteOrd.hpp>
+#include <signaldata/TransIdAI.hpp>
+#include <signaldata/DiGetNodes.hpp>
+#include <signaldata/DihScanTab.hpp>
+#include <signaldata/AttrInfo.hpp>
+#include <Interpreter.hpp>
+#include <AttributeHeader.hpp>
+#include <AttributeDescriptor.hpp>
+#include <KeyDescriptor.hpp>
+#include <md5_hash.hpp>
+#include <signaldata/TcKeyConf.hpp>
+
+#include <signaldata/NodeFailRep.hpp>
+#include <signaldata/ReadNodesConf.hpp>
+
+// Use DEBUG to print messages that should be
+// seen only when we debug the product
+
+#ifdef VM_TRACE
+
+#define DEBUG(x) ndbout << "DBSPJ: "<< x << endl;
+#define DEBUG_LQHKEYREQ
+#define DEBUG_SCAN_FRAGREQ
+
+#else
+
+#define DEBUG(x)
+
+#endif
+
+#if 1
+#define DEBUG_CRASH() ndbrequire(false)
+#else
+#define DEBUG_CRASH()
+#endif
+
+#if 1
+#undef DEBUG
+#define DEBUG(x)
+#undef DEBUG_LQHKEYREQ
+#undef DEBUG_SCAN_FRAGREQ
+#endif
+
+const Ptr<Dbspj::TreeNode> Dbspj::NullTreeNodePtr = { 0, RNIL };
+const Dbspj::RowRef Dbspj::NullRowRef = { RNIL, GLOBAL_PAGE_SIZE_WORDS, { 0 } };
+
+/** A noop for now.*/
+void Dbspj::execREAD_CONFIG_REQ(Signal* signal)
+{
+ jamEntry();
+ const ReadConfigReq req =
+ *reinterpret_cast<const ReadConfigReq*>(signal->getDataPtr());
+
+ Pool_context pc;
+ pc.m_block = this;
+
+ DEBUG("execREAD_CONFIG_REQ");
+ DEBUG("sizeof(Request): " << sizeof(Request) <<
+ " sizeof(TreeNode): " << sizeof(TreeNode));
+
+ m_arenaAllocator.init(1024, RT_SPJ_ARENA_BLOCK, pc);
+ m_request_pool.arena_pool_init(&m_arenaAllocator, RT_SPJ_REQUEST, pc);
+ m_treenode_pool.arena_pool_init(&m_arenaAllocator, RT_SPJ_TREENODE, pc);
+ m_scanfraghandle_pool.arena_pool_init(&m_arenaAllocator, RT_SPJ_SCANFRAG, pc);
+ m_lookup_request_hash.setSize(16);
+ m_scan_request_hash.setSize(16);
+ void* ptr = m_ctx.m_mm.get_memroot();
+ m_page_pool.set((RowPage*)ptr, (Uint32)~0);
+
+ Record_info ri;
+ Dependency_map::createRecordInfo(ri, RT_SPJ_DATABUFFER);
+ m_dependency_map_pool.init(&m_arenaAllocator, ri, pc);
+
+ ReadConfigConf* const conf =
+ reinterpret_cast<ReadConfigConf*>(signal->getDataPtrSend());
+ conf->senderRef = reference();
+ conf->senderData = req.senderData;
+
+ sendSignal(req.senderRef, GSN_READ_CONFIG_CONF, signal,
+ ReadConfigConf::SignalLength, JBB);
+}//Dbspj::execREAD_CONF_REQ()
+
+static Uint32 f_STTOR_REF = 0;
+
+void Dbspj::execSTTOR(Signal* signal)
+{
+//#define UNIT_TEST_DATABUFFER2
+
+ jamEntry();
+ /* START CASE */
+ const Uint16 tphase = signal->theData[1];
+ f_STTOR_REF = signal->getSendersBlockRef();
+
+ ndbout << "Dbspj::execSTTOR() inst:" << instance()
+ << " phase=" << tphase << endl;
+
+ if (tphase == 1)
+ {
+ jam();
+ signal->theData[0] = 0;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 1000, 1);
+ }
+
+ if (tphase == 4)
+ {
+ jam();
+
+ signal->theData[0] = reference();
+ sendSignal(NDBCNTR_REF, GSN_READ_NODESREQ, signal, 1, JBB);
+ return;
+ }
+
+ sendSTTORRY(signal);
+
+#ifdef UNIT_TEST_DATABUFFER2
+ if (tphase == 120)
+ {
+ ndbout_c("basic test of ArenaPool / DataBuffer2");
+
+ for (Uint32 i = 0; i<100; i++)
+ {
+ ArenaHead ah;
+ if (!m_arenaAllocator.seize(ah))
+ {
+ ndbout_c("Failed to allocate arena");
+ break;
+ }
+
+ ndbout_c("*** LOOP %u", i);
+ Uint32 sum = 0;
+ Dependency_map::Head head;
+ LocalArenaPoolImpl pool(ah, m_dependency_map_pool);
+ for (Uint32 j = 0; j<100; j++)
+ {
+ Uint32 sz = rand() % 1000;
+ if (0)
+ ndbout_c("adding %u", sz);
+ Local_dependency_map list(pool, head);
+ for (Uint32 i = 0; i<sz; i++)
+ signal->theData[i] = sum + i;
+ list.append(signal->theData, sz);
+ sum += sz;
+ }
+
+ {
+ ndbrequire(head.getSize() == sum);
+ Local_dependency_map list(pool, head);
+ Dependency_map::ConstDataBufferIterator it;
+ Uint32 cnt = 0;
+ for (list.first(it); !it.isNull(); list.next(it))
+ {
+ ndbrequire(* it.data == cnt);
+ cnt++;
+ }
+
+ ndbrequire(cnt == sum);
+ }
+
+ Resource_limit rl;
+ if (m_ctx.m_mm.get_resource_limit(7, rl))
+ {
+ ndbout_c("Resource %d min: %d max: %d curr: %d",
+ 7, rl.m_min, rl.m_max, rl.m_curr);
+ }
+
+ {
+ ndbout_c("release map");
+ Local_dependency_map list(pool, head);
+ list.release();
+ }
+
+ ndbout_c("release all");
+ m_arenaAllocator.release(ah);
+ ndbout_c("*** LOOP %u sum: %u", i, sum);
+ }
+ }
+#endif
+}//Dbspj::execSTTOR()
+
+void
+Dbspj::sendSTTORRY(Signal* signal)
+{
+ signal->theData[0] = 0;
+ signal->theData[1] = 0; /* BLOCK CATEGORY */
+ signal->theData[2] = 0; /* SIGNAL VERSION NUMBER */
+ signal->theData[3] = 4;
+#ifdef UNIT_TEST_DATABUFFER2
+ signal->theData[4] = 120; /* Start phase end*/
+#else
+ signal->theData[4] = 255;
+#endif
+ signal->theData[5] = 255;
+ sendSignal(f_STTOR_REF, GSN_STTORRY, signal, 6, JBB);
+}
+
+void
+Dbspj::execREAD_NODESCONF(Signal* signal)
+{
+ jamEntry();
+
+ ReadNodesConf * const conf = (ReadNodesConf *)signal->getDataPtr();
+
+ if (getNodeState().getNodeRestartInProgress())
+ {
+ jam();
+ c_alive_nodes.assign(NdbNodeBitmask::Size, conf->startedNodes);
+ c_alive_nodes.set(getOwnNodeId());
+ }
+ else
+ {
+ jam();
+ c_alive_nodes.assign(NdbNodeBitmask::Size, conf->startingNodes);
+ NdbNodeBitmask tmp;
+ tmp.assign(NdbNodeBitmask::Size, conf->startedNodes);
+ c_alive_nodes.bitOR(tmp);
+ }
+
+ sendSTTORRY(signal);
+}
+
+void
+Dbspj::execINCL_NODEREQ(Signal* signal)
+{
+ jamEntry();
+ const Uint32 senderRef = signal->theData[0];
+ const Uint32 nodeId = signal->theData[1];
+
+ ndbrequire(!c_alive_nodes.get(nodeId));
+ c_alive_nodes.set(nodeId);
+
+ signal->theData[0] = nodeId;
+ signal->theData[1] = reference();
+ sendSignal(senderRef, GSN_INCL_NODECONF, signal, 2, JBB);
+}
+
+void
+Dbspj::execNODE_FAILREP(Signal* signal)
+{
+ jamEntry();
+
+ const NodeFailRep * rep = (NodeFailRep*)signal->getDataPtr();
+ NdbNodeBitmask failed;
+ failed.assign(NdbNodeBitmask::Size, rep->theNodes);
+
+ c_alive_nodes.bitANDC(failed);
+
+ signal->theData[0] = 1;
+ signal->theData[1] = 0;
+ failed.copyto(NdbNodeBitmask::Size, signal->theData + 2);
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2 + NdbNodeBitmask::Size,
+ JBB);
+}
+
+void
+Dbspj::execAPI_FAILREQ(Signal* signal)
+{
+ jamEntry();
+ Uint32 failedApiNode = signal->theData[0];
+ ndbrequire(signal->theData[1] == QMGR_REF); // As callback hard-codes QMGR
+
+ /**
+ * We only need to care about lookups
+ * as SCAN's are aborted by DBTC
+ */
+
+ signal->theData[0] = failedApiNode;
+ signal->theData[1] = reference();
+ sendSignal(QMGR_REF, GSN_API_FAILCONF, signal, 2, JBB);
+}
+
+void
+Dbspj::execCONTINUEB(Signal* signal)
+{
+ jamEntry();
+ switch(signal->theData[0]) {
+ case 0:
+ releaseGlobal(signal);
+ return;
+ case 1:
+ nodeFail_checkRequests(signal);
+ return;
+ case 2:
+ nodeFail_checkRequests(signal);
+ return;
+ }
+
+ ndbrequire(false);
+}
+
+void
+Dbspj::nodeFail_checkRequests(Signal* signal)
+{
+ jam();
+ const Uint32 type = signal->theData[0];
+ const Uint32 bucket = signal->theData[1];
+
+ NdbNodeBitmask failed;
+ failed.assign(NdbNodeBitmask::Size, signal->theData+2);
+
+ Request_iterator iter;
+ Request_hash * hash;
+ switch(type){
+ case 1:
+ hash = &m_lookup_request_hash;
+ break;
+ case 2:
+ hash = &m_scan_request_hash;
+ break;
+ }
+ hash->next(bucket, iter);
+
+ const Uint32 RT_BREAK = 64;
+ for(Uint32 i = 0; (i<RT_BREAK || iter.bucket == bucket) &&
+ !iter.curr.isNull(); i++)
+ {
+ jam();
+
+ Ptr<Request> requestPtr = iter.curr;
+ hash->next(iter);
+ i += nodeFail(signal, requestPtr, failed);
+ }
+
+ if (!iter.curr.isNull())
+ {
+ jam();
+ signal->theData[0] = type;
+ signal->theData[1] = bucket;
+ failed.copyto(NdbNodeBitmask::Size, signal->theData+2);
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2 + NdbNodeBitmask::Size,
+ JBB);
+ }
+ else if (type == 1)
+ {
+ jam();
+ signal->theData[0] = 2;
+ signal->theData[1] = 0;
+ failed.copyto(NdbNodeBitmask::Size, signal->theData+2);
+ sendSignal(reference(), GSN_CONTINUEB, signal, 2 + NdbNodeBitmask::Size,
+ JBB);
+ }
+ else if (type == 2)
+ {
+ jam();
+ ndbout_c("Finished with handling node-failure");
+ }
+}
+
+/**
+ * MODULE LQHKEYREQ
+ */
+void Dbspj::execLQHKEYREQ(Signal* signal)
+{
+ jamEntry();
+ c_Counters.incr_counter(CI_READS_RECEIVED, 1);
+
+ const LqhKeyReq* req = reinterpret_cast<const LqhKeyReq*>(signal->getDataPtr());
+
+ /**
+ * #0 - KEYINFO contains key for first operation (used for hash in TC)
+ * #1 - ATTRINFO contains tree + parameters
+ * (unless StoredProcId is set, when only paramters are sent,
+ * but this is not yet implemented)
+ */
+ SectionHandle handle = SectionHandle(this, signal);
+ SegmentedSectionPtr ssPtr;
+ handle.getSection(ssPtr, LqhKeyReq::AttrInfoSectionNum);
+
+ Uint32 err;
+ Ptr<Request> requestPtr = { 0, RNIL };
+ do
+ {
+ ArenaHead ah;
+ err = DbspjErr::OutOfQueryMemory;
+ if (unlikely(!m_arenaAllocator.seize(ah)))
+ break;
+
+
+ m_request_pool.seize(ah, requestPtr);
+
+ new (requestPtr.p) Request(ah);
+ do_init(requestPtr.p, req, signal->getSendersBlockRef());
+
+ Uint32 len_cnt;
+
+ {
+ SectionReader r0(ssPtr, getSectionSegmentPool());
+
+ err = DbspjErr::ZeroLengthQueryTree;
+ if (unlikely(!r0.getWord(&len_cnt)))
+ break;
+ }
+
+ Uint32 len = QueryTree::getLength(len_cnt);
+ Uint32 cnt = QueryTree::getNodeCnt(len_cnt);
+
+ {
+ SectionReader treeReader(ssPtr, getSectionSegmentPool());
+ SectionReader paramReader(ssPtr, getSectionSegmentPool());
+ paramReader.step(len); // skip over tree to parameters
+
+ Build_context ctx;
+ ctx.m_resultRef = req->variableData[0];
+ ctx.m_savepointId = req->savePointId;
+ ctx.m_scanPrio = 1;
+ ctx.m_start_signal = signal;
+ ctx.m_keyPtr.i = handle.m_ptr[LqhKeyReq::KeyInfoSectionNum].i;
+ ctx.m_senderRef = signal->getSendersBlockRef();
+
+ err = build(ctx, requestPtr, treeReader, paramReader);
+ if (unlikely(err != 0))
+ break;
+ }
+
+ /**
+ * a query being shipped as a LQHKEYREQ may only return finite rows
+ * i.e be a (multi-)lookup
+ */
+ ndbassert(requestPtr.p->isLookup());
+ ndbassert(requestPtr.p->m_node_cnt == cnt);
+ err = DbspjErr::InvalidRequest;
+ if (unlikely(!requestPtr.p->isLookup() || requestPtr.p->m_node_cnt != cnt))
+ break;
+
+ /**
+ * Store request in list(s)/hash(es)
+ */
+ store_lookup(requestPtr);
+
+ release(ssPtr);
+ handle.clear();
+
+ start(signal, requestPtr);
+ return;
+ } while (0);
+
+ /**
+ * Error handling below,
+ * 'err' may contain error code.
+ */
+ if (!requestPtr.isNull())
+ {
+ jam();
+ m_request_pool.release(requestPtr);
+ }
+ releaseSections(handle);
+ handle_early_lqhkey_ref(signal, req, err);
+}
+
+void
+Dbspj::do_init(Request* requestP, const LqhKeyReq* req, Uint32 senderRef)
+{
+ requestP->m_bits = 0;
+ requestP->m_errCode = 0;
+ requestP->m_state = Request::RS_BUILDING;
+ requestP->m_node_cnt = 0;
+ requestP->m_cnt_active = 0;
+ requestP->m_rows = 0;
+ requestP->m_active_nodes.clear();
+ requestP->m_outstanding = 0;
+ requestP->m_transId[0] = req->transId1;
+ requestP->m_transId[1] = req->transId2;
+ bzero(requestP->m_lookup_node_data, sizeof(requestP->m_lookup_node_data));
+#ifdef SPJ_TRACE_TIME
+ requestP->m_cnt_batches = 0;
+ requestP->m_sum_rows = 0;
+ requestP->m_sum_running = 0;
+ requestP->m_sum_waiting = 0;
+ requestP->m_save_time = spj_now();
+#endif
+ const Uint32 reqInfo = req->requestInfo;
+ Uint32 tmp = req->clientConnectPtr;
+ if (LqhKeyReq::getDirtyFlag(reqInfo) &&
+ LqhKeyReq::getOperation(reqInfo) == ZREAD)
+ {
+ jam();
+
+ ndbrequire(LqhKeyReq::getApplicationAddressFlag(reqInfo));
+ //const Uint32 apiRef = lqhKeyReq->variableData[0];
+ //const Uint32 apiOpRec = lqhKeyReq->variableData[1];
+ tmp = req->variableData[1];
+ requestP->m_senderData = tmp;
+ requestP->m_senderRef = senderRef;
+ }
+ else
+ {
+ if (LqhKeyReq::getSameClientAndTcFlag(reqInfo) == 1)
+ {
+ if (LqhKeyReq::getApplicationAddressFlag(reqInfo))
+ tmp = req->variableData[2];
+ else
+ tmp = req->variableData[0];
+ }
+ requestP->m_senderData = tmp;
+ requestP->m_senderRef = senderRef;
+ }
+ requestP->m_rootResultData = tmp;
+}
+
+void
+Dbspj::store_lookup(Ptr<Request> requestPtr)
+{
+ ndbassert(requestPtr.p->isLookup());
+ Ptr<Request> tmp;
+ bool found = m_lookup_request_hash.find(tmp, *requestPtr.p);
+ ndbrequire(found == false);
+ m_lookup_request_hash.add(requestPtr);
+}
+
+void
+Dbspj::handle_early_lqhkey_ref(Signal* signal,
+ const LqhKeyReq * lqhKeyReq,
+ Uint32 err)
+{
+ /**
+ * Error path...
+ */
+ ndbrequire(err);
+ const Uint32 reqInfo = lqhKeyReq->requestInfo;
+ const Uint32 transid[2] = { lqhKeyReq->transId1, lqhKeyReq->transId2 };
+
+ if (LqhKeyReq::getDirtyFlag(reqInfo) &&
+ LqhKeyReq::getOperation(reqInfo) == ZREAD)
+ {
+ jam();
+ /* Dirty read sends TCKEYREF direct to client, and nothing to TC */
+ ndbrequire(LqhKeyReq::getApplicationAddressFlag(reqInfo));
+ const Uint32 apiRef = lqhKeyReq->variableData[0];
+ const Uint32 apiOpRec = lqhKeyReq->variableData[1];
+
+ TcKeyRef* const tcKeyRef = reinterpret_cast<TcKeyRef*>(signal->getDataPtrSend());
+
+ tcKeyRef->connectPtr = apiOpRec;
+ tcKeyRef->transId[0] = transid[0];
+ tcKeyRef->transId[1] = transid[1];
+ tcKeyRef->errorCode = err;
+ sendTCKEYREF(signal, apiRef, signal->getSendersBlockRef());
+ }
+ else
+ {
+ jam();
+ const Uint32 returnref = signal->getSendersBlockRef();
+ const Uint32 clientPtr = lqhKeyReq->clientConnectPtr;
+
+ Uint32 TcOprec = clientPtr;
+ if (LqhKeyReq::getSameClientAndTcFlag(reqInfo) == 1)
+ {
+ if (LqhKeyReq::getApplicationAddressFlag(reqInfo))
+ TcOprec = lqhKeyReq->variableData[2];
+ else
+ TcOprec = lqhKeyReq->variableData[0];
+ }
+
+ LqhKeyRef* const ref = reinterpret_cast<LqhKeyRef*>(signal->getDataPtrSend());
+ ref->userRef = clientPtr;
+ ref->connectPtr = TcOprec;
+ ref->errorCode = err;
+ ref->transId1 = transid[0];
+ ref->transId2 = transid[1];
+ sendSignal(returnref, GSN_LQHKEYREF, signal,
+ LqhKeyRef::SignalLength, JBB);
+ }
+}
+
+void
+Dbspj::sendTCKEYREF(Signal* signal, Uint32 ref, Uint32 routeRef)
+{
+ const Uint32 nodeId = refToNode(ref);
+ const bool connectedToNode = getNodeInfo(nodeId).m_connected;
+
+ if (likely(connectedToNode))
+ {
+ jam();
+ sendSignal(ref, GSN_TCKEYREF, signal, TcKeyRef::SignalLength, JBB);
+ }
+ else
+ {
+ jam();
+ memmove(signal->theData+25, signal->theData, 4*TcKeyRef::SignalLength);
+ RouteOrd* ord = (RouteOrd*)signal->getDataPtrSend();
+ ord->dstRef = ref;
+ ord->srcRef = reference();
+ ord->gsn = GSN_TCKEYREF;
+ ord->cnt = 0;
+ LinearSectionPtr ptr[3];
+ ptr[0].p = signal->theData+25;
+ ptr[0].sz = TcKeyRef::SignalLength;
+ sendSignal(routeRef, GSN_ROUTE_ORD, signal, RouteOrd::SignalLength, JBB,
+ ptr, 1);
+ }
+}
+
+void
+Dbspj::sendTCKEYCONF(Signal* signal, Uint32 len, Uint32 ref, Uint32 routeRef)
+{
+ const Uint32 nodeId = refToNode(ref);
+ const bool connectedToNode = getNodeInfo(nodeId).m_connected;
+
+ if (likely(connectedToNode))
+ {
+ jam();
+ sendSignal(ref, GSN_TCKEYCONF, signal, len, JBB);
+ }
+ else
+ {
+ jam();
+ memmove(signal->theData+25, signal->theData, 4*len);
+ RouteOrd* ord = (RouteOrd*)signal->getDataPtrSend();
+ ord->dstRef = ref;
+ ord->srcRef = reference();
+ ord->gsn = GSN_TCKEYCONF;
+ ord->cnt = 0;
+ LinearSectionPtr ptr[3];
+ ptr[0].p = signal->theData+25;
+ ptr[0].sz = len;
+ sendSignal(routeRef, GSN_ROUTE_ORD, signal, RouteOrd::SignalLength, JBB,
+ ptr, 1);
+ }
+}
+
+/**
+ * END - MODULE LQHKEYREQ
+ */
+
+
+/**
+ * MODULE SCAN_FRAGREQ
+ */
+void
+Dbspj::execSCAN_FRAGREQ(Signal* signal)
+{
+ jamEntry();
+
+ /* Reassemble if the request was fragmented */
+ if (!assembleFragments(signal))
+ {
+ jam();
+ return;
+ }
+
+ const ScanFragReq * req = (ScanFragReq *)&signal->theData[0];
+
+#ifdef DEBUG_SCAN_FRAGREQ
+ ndbout_c("Incomming SCAN_FRAGREQ ");
+ printSCAN_FRAGREQ(stdout, signal->getDataPtrSend(),
+ ScanFragReq::SignalLength + 2,
+ DBLQH);
+#endif
+
+ /**
+ * #0 - ATTRINFO contains tree + parameters
+ * (unless StoredProcId is set, when only paramters are sent,
+ * but this is not yet implemented)
+ * #1 - KEYINFO if first op is index scan - contains bounds for first scan
+ * if first op is lookup - contains keyinfo for lookup
+ */
+ SectionHandle handle = SectionHandle(this, signal);
+ SegmentedSectionPtr ssPtr;
+ handle.getSection(ssPtr, ScanFragReq::AttrInfoSectionNum);
+
+ Uint32 err;
+ Ptr<Request> requestPtr = { 0, RNIL };
+ do
+ {
+ ArenaHead ah;
+ err = DbspjErr::OutOfQueryMemory;
+ if (unlikely(!m_arenaAllocator.seize(ah)))
+ break;
+
+ m_request_pool.seize(ah, requestPtr);
+
+ new (requestPtr.p) Request(ah);
+ do_init(requestPtr.p, req, signal->getSendersBlockRef());
+
+ Uint32 len_cnt;
+ {
+ SectionReader r0(ssPtr, getSectionSegmentPool());
+ err = DbspjErr::ZeroLengthQueryTree;
+ if (unlikely(!r0.getWord(&len_cnt)))
+ break;
+ }
+
+ Uint32 len = QueryTree::getLength(len_cnt);
+ Uint32 cnt = QueryTree::getNodeCnt(len_cnt);
+
+ {
+ SectionReader treeReader(ssPtr, getSectionSegmentPool());
+ SectionReader paramReader(ssPtr, getSectionSegmentPool());
+ paramReader.step(len); // skip over tree to parameters
+
+ Build_context ctx;
+ ctx.m_resultRef = req->resultRef;
+ ctx.m_scanPrio = ScanFragReq::getScanPrio(req->requestInfo);
+ ctx.m_savepointId = req->savePointId;
+ ctx.m_batch_size_rows = req->batch_size_rows;
+ ctx.m_start_signal = signal;
+ ctx.m_senderRef = signal->getSendersBlockRef();
+
+ if (handle.m_cnt > 1)
+ {
+ jam();
+ ctx.m_keyPtr.i = handle.m_ptr[ScanFragReq::KeyInfoSectionNum].i;
+ }
+ else
+ {
+ jam();
+ ctx.m_keyPtr.i = RNIL;
+ }
+
+ err = build(ctx, requestPtr, treeReader, paramReader);
+ if (unlikely(err != 0))
+ break;
+ }
+
+ ndbassert(requestPtr.p->isScan());
+ ndbassert(requestPtr.p->m_node_cnt == cnt);
+ err = DbspjErr::InvalidRequest;
+ if (unlikely(!requestPtr.p->isScan() || requestPtr.p->m_node_cnt != cnt))
+ break;
+
+ /**
+ * Store request in list(s)/hash(es)
+ */
+ store_scan(requestPtr);
+
+ release(ssPtr);
+ handle.clear();
+
+ start(signal, requestPtr);
+ return;
+ } while (0);
+
+ if (!requestPtr.isNull())
+ {
+ jam();
+ m_request_pool.release(requestPtr);
+ }
+ releaseSections(handle);
+ handle_early_scanfrag_ref(signal, req, err);
+}
+
+void
+Dbspj::do_init(Request* requestP, const ScanFragReq* req, Uint32 senderRef)
+{
+ requestP->m_bits = 0;
+ requestP->m_errCode = 0;
+ requestP->m_state = Request::RS_BUILDING;
+ requestP->m_node_cnt = 0;
+ requestP->m_cnt_active = 0;
+ requestP->m_rows = 0;
+ requestP->m_active_nodes.clear();
+ requestP->m_outstanding = 0;
+ requestP->m_senderRef = senderRef;
+ requestP->m_senderData = req->senderData;
+ requestP->m_transId[0] = req->transId1;
+ requestP->m_transId[1] = req->transId2;
+ requestP->m_rootResultData = req->resultData;
+ bzero(requestP->m_lookup_node_data, sizeof(requestP->m_lookup_node_data));
+#ifdef SPJ_TRACE_TIME
+ requestP->m_cnt_batches = 0;
+ requestP->m_sum_rows = 0;
+ requestP->m_sum_running = 0;
+ requestP->m_sum_waiting = 0;
+ requestP->m_save_time = spj_now();
+#endif
+}
+
+void
+Dbspj::store_scan(Ptr<Request> requestPtr)
+{
+ ndbassert(requestPtr.p->isScan());
+ Ptr<Request> tmp;
+ bool found = m_scan_request_hash.find(tmp, *requestPtr.p);
+ ndbrequire(found == false);
+ m_scan_request_hash.add(requestPtr);
+}
+
+void
+Dbspj::handle_early_scanfrag_ref(Signal* signal,
+ const ScanFragReq * _req,
+ Uint32 err)
+{
+ ScanFragReq req = *_req;
+ Uint32 senderRef = signal->getSendersBlockRef();
+
+ ScanFragRef * ref = (ScanFragRef*)&signal->theData[0];
+ ref->senderData = req.senderData;
+ ref->transId1 = req.transId1;
+ ref->transId2 = req.transId2;
+ ref->errorCode = err;
+ sendSignal(senderRef, GSN_SCAN_FRAGREF, signal,
+ ScanFragRef::SignalLength, JBB);
+}
+
+/**
+ * END - MODULE SCAN_FRAGREQ
+ */
+
+/**
+ * MODULE GENERIC
+ */
+Uint32
+Dbspj::build(Build_context& ctx,
+ Ptr<Request> requestPtr,
+ SectionReader & tree,
+ SectionReader & param)
+{
+ Uint32 tmp0, tmp1;
+ Uint32 err = DbspjErr::ZeroLengthQueryTree;
+ ctx.m_cnt = 0;
+ ctx.m_scan_cnt = 0;
+
+ tree.getWord(&tmp0);
+ Uint32 loop = QueryTree::getNodeCnt(tmp0);
+
+ DEBUG("::build()");
+ err = DbspjErr::InvalidTreeNodeCount;
+ if (loop == 0 || loop > NDB_SPJ_MAX_TREE_NODES)
+ {
+ DEBUG_CRASH();
+ goto error;
+ }
+
+ while (ctx.m_cnt < loop)
+ {
+ DEBUG(" - loop " << ctx.m_cnt << " pos: " << tree.getPos().currPos);
+ tree.peekWord(&tmp0);
+ param.peekWord(&tmp1);
+ Uint32 node_op = QueryNode::getOpType(tmp0);
+ Uint32 node_len = QueryNode::getLength(tmp0);
+ Uint32 param_op = QueryNodeParameters::getOpType(tmp1);
+ Uint32 param_len = QueryNodeParameters::getLength(tmp1);
+
+ err = DbspjErr::QueryNodeTooBig;
+ if (unlikely(node_len >= NDB_ARRAY_SIZE(m_buffer0)))
+ {
+ DEBUG_CRASH();
+ goto error;
+ }
+
+ err = DbspjErr::QueryNodeParametersTooBig;
+ if (unlikely(param_len >= NDB_ARRAY_SIZE(m_buffer1)))
+ {
+ DEBUG_CRASH();
+ goto error;
+ }
+
+ err = DbspjErr::InvalidTreeNodeSpecification;
+ if (unlikely(tree.getWords(m_buffer0, node_len) == false))
+ {
+ DEBUG_CRASH();
+ goto error;
+ }
+
+ err = DbspjErr::InvalidTreeParametersSpecification;
+ if (unlikely(param.getWords(m_buffer1, param_len) == false))
+ {
+ DEBUG_CRASH();
+ goto error;
+ }
+
+#if defined(DEBUG_LQHKEYREQ) || defined(DEBUG_SCAN_FRAGREQ)
+ printf("node: ");
+ for (Uint32 i = 0; i<node_len; i++)
+ printf("0x%.8x ", m_buffer0[i]);
+ printf("\n");
+
+ printf("param: ");
+ for (Uint32 i = 0; i<param_len; i++)
+ printf("0x%.8x ", m_buffer1[i]);
+ printf("\n");
+#endif
+
+ err = DbspjErr::UnknowQueryOperation;
+ if (unlikely(node_op != param_op))
+ {
+ DEBUG_CRASH();
+ goto error;
+ }
+
+ const OpInfo* info = getOpInfo(node_op);
+ if (unlikely(info == 0))
+ {
+ DEBUG_CRASH();
+ goto error;
+ }
+
+ QueryNode* qn = (QueryNode*)m_buffer0;
+ QueryNodeParameters * qp = (QueryNodeParameters*)m_buffer1;
+ qn->len = node_len;
+ qp->len = param_len;
+ err = (this->*(info->m_build))(ctx, requestPtr, qn, qp);
+ if (unlikely(err != 0))
+ {
+ DEBUG_CRASH();
+ goto error;
+ }
+
+ /**
+ * only first node gets access to signal
+ */
+ ctx.m_start_signal = 0;
+
+ /**
+ * TODO handle error, by aborting request
+ */
+ ndbrequire(ctx.m_cnt < NDB_ARRAY_SIZE(ctx.m_node_list));
+ ctx.m_cnt++;
+ }
+ requestPtr.p->m_node_cnt = ctx.m_cnt;
+
+ /**
+ * Init ROW_BUFFERS for those TreeNodes requiring either
+ * T_ROW_BUFFER or T_ROW_BUFFER_MAP.
+ */
+ if (requestPtr.p->m_bits & Request::RT_ROW_BUFFERS)
+ {
+ Ptr<TreeNode> treeNodePtr;
+ Local_TreeNode_list list(m_treenode_pool, requestPtr.p->m_nodes);
+ for (list.first(treeNodePtr); !treeNodePtr.isNull(); list.next(treeNodePtr))
+ {
+ if (treeNodePtr.p->m_bits & TreeNode::T_ROW_BUFFER_MAP)
+ {
+ jam();
+ treeNodePtr.p->m_row_map.init();
+ }
+ else if (treeNodePtr.p->m_bits & TreeNode::T_ROW_BUFFER)
+ {
+ jam();
+ treeNodePtr.p->m_row_list.init();
+ }
+ }
+ }
+
+ if (ctx.m_scan_cnt > 1)
+ {
+ jam();
+ requestPtr.p->m_bits |= Request::RT_MULTI_SCAN;
+
+ /**
+ * Iff, multi-scan is non-bushy (normal case)
+ * we don't strictly need RT_VAR_ALLOC for RT_ROW_BUFFERS
+ * but could instead pop-row stack frame,
+ * however this is not implemented...
+ *
+ * so, use RT_VAR_ALLOC
+ */
+ if (requestPtr.p->m_bits & Request::RT_ROW_BUFFERS)
+ {
+ jam();
+ requestPtr.p->m_bits |= Request::RT_VAR_ALLOC;
+ }
+
+ {
+ /**
+ * If multi scan, then cursors are determined when one batch is complete
+ * hence clear list here...
+ * But if it's single scan...the list will already contain the
+ * only scan in the tree
+ */
+ Local_TreeNodeCursor_list list(m_treenode_pool,
+ requestPtr.p->m_cursor_nodes);
+ ndbassert(list.noOfElements() > 1);
+ list.remove();
+ }
+ }
+
+ return 0;
+
+error:
+ jam();
+ return err;
+}
+
+Uint32
+Dbspj::createNode(Build_context& ctx, Ptr<Request> requestPtr,
+ Ptr<TreeNode> & treeNodePtr)
+{
+ /**
+ * In the future, we can have different TreeNode-allocation strategies
+ * that can be setup using the Build_context
+ *
+ */
+ if (m_treenode_pool.seize(requestPtr.p->m_arena, treeNodePtr))
+ {
+ DEBUG("createNode - seize -> ptrI: " << treeNodePtr.i);
+ new (treeNodePtr.p) TreeNode(requestPtr.i);
+ ctx.m_node_list[ctx.m_cnt] = treeNodePtr;
+ Local_TreeNode_list list(m_treenode_pool, requestPtr.p->m_nodes);
+ list.addLast(treeNodePtr);
+ treeNodePtr.p->m_node_no = ctx.m_cnt;
+ return 0;
+ }
+ return DbspjErr::OutOfOperations;
+}
+
+void
+Dbspj::start(Signal* signal,
+ Ptr<Request> requestPtr)
+{
+ if (requestPtr.p->m_bits & Request::RT_NEED_PREPARE)
+ {
+ jam();
+ requestPtr.p->m_outstanding = 0;
+ requestPtr.p->m_state = Request::RS_PREPARING;
+
+ Ptr<TreeNode> nodePtr;
+ Local_TreeNode_list list(m_treenode_pool, requestPtr.p->m_nodes);
+ for (list.first(nodePtr); !nodePtr.isNull(); list.next(nodePtr))
+ {
+ jam();
+ ndbrequire(nodePtr.p->m_info != 0);
+ if (nodePtr.p->m_info->m_prepare != 0)
+ {
+ jam();
+ (this->*(nodePtr.p->m_info->m_prepare))(signal, requestPtr, nodePtr);
+ }
+ }
+
+ /**
+ * preferably RT_NEED_PREPARE should only be set if blocking
+ * calls are used, in which case m_outstanding should have been increased
+ */
+ ndbassert(requestPtr.p->m_outstanding);
+ }
+
+ checkPrepareComplete(signal, requestPtr, 0);
+}
+
+void
+Dbspj::checkPrepareComplete(Signal * signal, Ptr<Request> requestPtr,
+ Uint32 cnt)
+{
+ ndbrequire(requestPtr.p->m_outstanding >= cnt);
+ requestPtr.p->m_outstanding -= cnt;
+
+ if (requestPtr.p->m_outstanding == 0)
+ {
+ jam();
+
+ if (unlikely((requestPtr.p->m_state & Request::RS_ABORTING) != 0))
+ {
+ jam();
+ batchComplete(signal, requestPtr);
+ return;
+ }
+
+ requestPtr.p->m_state = Request::RS_RUNNING;
+ Ptr<TreeNode> nodePtr;
+ {
+ Local_TreeNode_list list(m_treenode_pool, requestPtr.p->m_nodes);
+ ndbrequire(list.first(nodePtr));
+ }
+ ndbrequire(nodePtr.p->m_info != 0 && nodePtr.p->m_info->m_start != 0);
+ (this->*(nodePtr.p->m_info->m_start))(signal, requestPtr, nodePtr);
+ }
+}
+
+void
+Dbspj::checkBatchComplete(Signal * signal, Ptr<Request> requestPtr,
+ Uint32 cnt)
+{
+ ndbrequire(requestPtr.p->m_outstanding >= cnt);
+ requestPtr.p->m_outstanding -= cnt;
+
+ if (requestPtr.p->m_outstanding == 0)
+ {
+ jam();
+ batchComplete(signal, requestPtr);
+ }
+}
+
+void
+Dbspj::batchComplete(Signal* signal, Ptr<Request> requestPtr)
+{
+ ndbrequire(requestPtr.p->m_outstanding == 0); // "definition" of batchComplete
+
+ bool is_complete = requestPtr.p->m_cnt_active == 0;
+ bool need_complete_phase = requestPtr.p->m_bits & Request::RT_NEED_COMPLETE;
+
+ if (requestPtr.p->isLookup())
+ {
+ ndbassert(requestPtr.p->m_cnt_active == 0);
+ }
+
+ if (!is_complete || (is_complete && need_complete_phase == false))
+ {
+ /**
+ * one batch complete, and either
+ * - request not complete
+ * - or not complete_phase needed
+ */
+ jam();
+
+ if ((requestPtr.p->m_state & Request::RS_ABORTING) != 0)
+ {
+ ndbassert(is_complete);
+ }
+ sendConf(signal, requestPtr, is_complete);
+ }
+ else if (is_complete && need_complete_phase)
+ {
+ jam();
+ /**
+ * run complete-phase
+ */
+ complete(signal, requestPtr);
+ return;
+ }
+
+ if (requestPtr.p->m_cnt_active == 0)
+ {
+ jam();
+ /**
+ * request completed
+ */
+ cleanup(requestPtr);
+ }
+ else if ((requestPtr.p->m_bits & Request::RT_MULTI_SCAN) != 0)
+ {
+ jam();
+ /**
+ * release unneeded buffers and position cursor for SCAN_NEXTREQ
+ */
+ releaseScanBuffers(requestPtr);
+ }
+ else if ((requestPtr.p->m_bits & Request::RT_ROW_BUFFERS) != 0)
+ {
+ jam();
+ /**
+ * if not multiple scans in request, simply release all pages allocated
+ * for row buffers (all rows will be released anyway)
+ */
+ releaseRequestBuffers(requestPtr, true);
+ }
+}
+
+void
+Dbspj::sendConf(Signal* signal, Ptr<Request> requestPtr, bool is_complete)
+{
+ if (requestPtr.p->isScan())
+ {
+ if (unlikely((requestPtr.p->m_state & Request::RS_WAITING) != 0))
+ {
+ jam();
+ /**
+ * We aborted request ourselves (due to node-failure ?)
+ * but TC haven't contacted us...so we can't reply yet...
+ */
+ ndbrequire(is_complete);
+ ndbrequire((requestPtr.p->m_state & Request::RS_ABORTING) != 0);
+ return;
+ }
+
+ if (requestPtr.p->m_errCode == 0)
+ {
+ jam();
+ ScanFragConf * conf=
+ reinterpret_cast<ScanFragConf*>(signal->getDataPtrSend());
+ conf->senderData = requestPtr.p->m_senderData;
+ conf->transId1 = requestPtr.p->m_transId[0];
+ conf->transId2 = requestPtr.p->m_transId[1];
+ conf->completedOps = requestPtr.p->m_rows;
+ conf->fragmentCompleted = is_complete ? 1 : 0;
+ conf->total_len = requestPtr.p->m_active_nodes.rep.data[0];
+
+ c_Counters.incr_counter(CI_SCAN_BATCHES_RETURNED, 1);
+ c_Counters.incr_counter(CI_SCAN_ROWS_RETURNED, requestPtr.p->m_rows);
+
+#ifdef SPJ_TRACE_TIME
+ Uint64 now = spj_now();
+ Uint64 then = requestPtr.p->m_save_time;
+
+ requestPtr.p->m_sum_rows += requestPtr.p->m_rows;
+ requestPtr.p->m_sum_running += Uint32(now - then);
+ requestPtr.p->m_cnt_batches++;
+ requestPtr.p->m_save_time = now;
+
+ if (is_complete)
+ {
+ Uint32 cnt = requestPtr.p->m_cnt_batches;
+ ndbout_c("batches: %u avg_rows: %u avg_running: %u avg_wait: %u",
+ cnt,
+ (requestPtr.p->m_sum_rows / cnt),
+ (requestPtr.p->m_sum_running / cnt),
+ cnt == 1 ? 0 : requestPtr.p->m_sum_waiting / (cnt - 1));
+ }
+#endif
+
+ /**
+ * reset for next batch
+ */
+ requestPtr.p->m_rows = 0;
+ if (!is_complete)
+ {
+ jam();
+ requestPtr.p->m_state |= Request::RS_WAITING;
+ }
+#ifdef DEBUG_SCAN_FRAGREQ
+ ndbout_c("Dbspj::sendConf() sending SCAN_FRAGCONF ");
+ printSCAN_FRAGCONF(stdout, signal->getDataPtrSend(),
+ conf->total_len,
+ DBLQH);
+#endif
+ sendSignal(requestPtr.p->m_senderRef, GSN_SCAN_FRAGCONF, signal,
+ ScanFragConf::SignalLength, JBB);
+ }
+ else
+ {
+ jam();
+ ndbrequire(is_complete);
+ ScanFragRef * ref=
+ reinterpret_cast<ScanFragRef*>(signal->getDataPtrSend());
+ ref->senderData = requestPtr.p->m_senderData;
+ ref->transId1 = requestPtr.p->m_transId[0];
+ ref->transId2 = requestPtr.p->m_transId[1];
+ ref->errorCode = requestPtr.p->m_errCode;
+
+ sendSignal(requestPtr.p->m_senderRef, GSN_SCAN_FRAGREF, signal,
+ ScanFragRef::SignalLength, JBB);
+ }
+ }
+ else
+ {
+ ndbassert(is_complete);
+ if (requestPtr.p->m_errCode)
+ {
+ jam();
+ Uint32 resultRef = getResultRef(requestPtr);
+ TcKeyRef* ref = (TcKeyRef*)signal->getDataPtr();
+ ref->connectPtr = requestPtr.p->m_senderData;
+ ref->transId[0] = requestPtr.p->m_transId[0];
+ ref->transId[1] = requestPtr.p->m_transId[1];
+ ref->errorCode = requestPtr.p->m_errCode;
+ ref->errorData = 0;
+
+ sendTCKEYREF(signal, resultRef, requestPtr.p->m_senderRef);
+ }
+ }
+}
+
+Uint32
+Dbspj::getResultRef(Ptr<Request> requestPtr)
+{
+ Ptr<TreeNode> nodePtr;
+ Local_TreeNode_list list(m_treenode_pool, requestPtr.p->m_nodes);
+ for (list.first(nodePtr); !nodePtr.isNull(); list.next(nodePtr))
+ {
+ if (nodePtr.p->m_info == &g_LookupOpInfo)
+ {
+ jam();
+ return nodePtr.p->m_lookup_data.m_api_resultRef;
+ }
+ }
+ ndbrequire(false);
+ return 0;
+}
+
+void
+Dbspj::releaseScanBuffers(Ptr<Request> requestPtr)
+{
+ Ptr<TreeNode> treeNodePtr;
+ {
+ Local_TreeNode_list list(m_treenode_pool, requestPtr.p->m_nodes);
+ list.first(treeNodePtr);
+ }
+
+ /**
+ * This is calling recursive function...buh!
+ * but i can't figure out how to do it someother way...
+ */
+
+ /**
+ * This recursive function will register nodes to be notified
+ * about SCAN_NEXTREQ.
+ *
+ * Clear it first, so that nodes won't end up in it several times...
+ */
+ requestPtr.p->m_cursor_nodes.init();
+
+ /**
+ * Needs to be atleast 1 active otherwise we should have
+ * taken the cleanup "path" in batchComplete
+ */
+ ndbrequire(releaseScanBuffers(requestPtr, treeNodePtr) > 0);
+}
+
+void
+Dbspj::mark_active(Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr,
+ bool value)
+{
+ Uint32 bit = treeNodePtr.p->m_node_no;
+ if (value)
+ {
+ ndbassert(requestPtr.p->m_active_nodes.get(bit) == false);
+ }
+ else
+ {
+ ndbassert(requestPtr.p->m_active_nodes.get(bit) == true);
+ }
+ requestPtr.p->m_active_nodes.set(bit, value);
+}
+
+void
+Dbspj::registerCursor(Ptr<Request> requestPtr, Ptr<TreeNode> treeNodePtr)
+{
+ Local_TreeNodeCursor_list list(m_treenode_pool, requestPtr.p->m_cursor_nodes);
+#ifdef VM_TRACE
+ {
+ Ptr<TreeNode> nodePtr;
+ for (list.first(nodePtr); !nodePtr.isNull(); list.next(nodePtr))
+ {
+ ndbrequire(nodePtr.i != treeNodePtr.i);
+ }
+ }
+#endif
+ list.add(treeNodePtr);
+}
+
+Uint32
+Dbspj::releaseScanBuffers(Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ Uint32 active_child = 0;
+
+ LocalArenaPoolImpl pool(requestPtr.p->m_arena, m_dependency_map_pool);
+ Local_dependency_map list(pool, treeNodePtr.p->m_dependent_nodes);
+ Dependency_map::ConstDataBufferIterator it;
+ for (list.first(it); !it.isNull(); list.next(it))
+ {
+ jam();
+ Ptr<TreeNode> childPtr;
+ m_treenode_pool.getPtr(childPtr, * it.data);
+ active_child += releaseScanBuffers(requestPtr, childPtr);
+ }
+
+ const bool active = treeNodePtr.p->m_state == TreeNode::TN_ACTIVE;
+ if (active_child == 0)
+ {
+ jam();
+
+ /**
+ * If there is no active children,
+ * then we can release our own (optionally) buffered rows
+ */
+ if (treeNodePtr.p->m_bits & TreeNode::T_ROW_BUFFER)
+ {
+ jam();
+ releaseNodeRows(requestPtr, treeNodePtr);
+ }
+
+ /**
+ * If we have no active children,
+ * and we ourself is active (i.e not consumed all rows originating
+ * from parent rows)
+ *
+ * Then, this is a position that execSCAN_NEXTREQ should continue
+ */
+ if (active)
+ {
+ jam();
+ registerCursor(requestPtr, treeNodePtr);
+ }
+ }
+
+ return active_child + (active ? 1 : 0);
+}
+
+void
+Dbspj::releaseNodeRows(Ptr<Request> requestPtr, Ptr<TreeNode> treeNodePtr)
+{
+ /**
+ * Release all rows associated with tree node
+ */
+
+ // only when var-alloc, or else stack will be popped wo/ consideration
+ // to individual rows
+ ndbassert(requestPtr.p->m_bits & Request::RT_VAR_ALLOC);
+ ndbassert(treeNodePtr.p->m_bits & TreeNode::T_ROW_BUFFER);
+
+ /**
+ * Two ways to iterate...
+ */
+ if ((treeNodePtr.p->m_bits & TreeNode::T_ROW_BUFFER_MAP) == 0)
+ {
+ jam();
+ Uint32 cnt = 0;
+ SLFifoRowListIterator iter;
+ for (first(requestPtr, treeNodePtr, iter); !iter.isNull(); )
+ {
+ jam();
+ RowRef pos = iter.m_ref;
+ next(iter);
+ releaseRow(requestPtr, pos);
+ cnt ++;
+ }
+ treeNodePtr.p->m_row_list.init();
+ DEBUG("SLFifoRowListIterator: released " << cnt << " rows!");
+ }
+ else
+ {
+ jam();
+ Uint32 cnt = 0;
+ RowMapIterator iter;
+ for (first(requestPtr, treeNodePtr, iter); !iter.isNull(); )
+ {
+ jam();
+ RowRef pos = iter.m_ref;
+ // this could be made more efficient by not actually seting up m_row_ptr
+ next(iter);
+ releaseRow(requestPtr, pos);
+ cnt++;
+ }
+ treeNodePtr.p->m_row_map.init();
+ DEBUG("RowMapIterator: released " << cnt << " rows!");
+ }
+}
+
+void
+Dbspj::releaseRow(Ptr<Request> requestPtr, RowRef pos)
+{
+ ndbassert(requestPtr.p->m_bits & Request::RT_VAR_ALLOC);
+ ndbassert(pos.m_allocator == 1);
+ Ptr<RowPage> ptr;
+ m_page_pool.getPtr(ptr, pos.m_page_id);
+ ((Var_page*)ptr.p)->free_record(pos.m_page_pos, Var_page::CHAIN);
+ Uint32 free_space = ((Var_page*)ptr.p)->free_space;
+ if (free_space == 0)
+ {
+ jam();
+ LocalDLFifoList<RowPage> list(m_page_pool,
+ requestPtr.p->m_rowBuffer.m_page_list);
+ list.remove(ptr);
+ releasePage(ptr);
+ }
+ else if (free_space > requestPtr.p->m_rowBuffer.m_var.m_free)
+ {
+ LocalDLFifoList<RowPage> list(m_page_pool,
+ requestPtr.p->m_rowBuffer.m_page_list);
+ list.remove(ptr);
+ list.addLast(ptr);
+ requestPtr.p->m_rowBuffer.m_var.m_free = free_space;
+ }
+}
+
+void
+Dbspj::releaseRequestBuffers(Ptr<Request> requestPtr, bool reset)
+{
+ /**
+ * Release all pages for request
+ */
+ {
+ {
+ LocalDLFifoList<RowPage> list(m_page_pool,
+ requestPtr.p->m_rowBuffer.m_page_list);
+ if (!list.isEmpty())
+ {
+ jam();
+ Ptr<RowPage> first, last;
+ list.first(first);
+ list.last(last);
+ releasePages(first.i, last);
+ list.remove();
+ }
+ }
+ requestPtr.p->m_rowBuffer.stack_init();
+ }
+
+ if (reset)
+ {
+ Ptr<TreeNode> nodePtr;
+ Local_TreeNode_list list(m_treenode_pool, requestPtr.p->m_nodes);
+ for (list.first(nodePtr); !nodePtr.isNull(); list.next(nodePtr))
+ {
+ jam();
+ if (nodePtr.p->m_bits & TreeNode::T_ROW_BUFFER)
+ {
+ jam();
+ if (nodePtr.p->m_bits & TreeNode::T_ROW_BUFFER_MAP)
+ {
+ jam();
+ nodePtr.p->m_row_map.init();
+ }
+ else
+ {
+ nodePtr.p->m_row_list.init();
+ }
+ }
+ }
+ }
+}
+
+void
+Dbspj::reportBatchComplete(Signal * signal, Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ LocalArenaPoolImpl pool(requestPtr.p->m_arena, m_dependency_map_pool);
+ Local_dependency_map list(pool, treeNodePtr.p->m_dependent_nodes);
+ Dependency_map::ConstDataBufferIterator it;
+ for (list.first(it); !it.isNull(); list.next(it))
+ {
+ jam();
+ Ptr<TreeNode> childPtr;
+ m_treenode_pool.getPtr(childPtr, * it.data);
+ if (childPtr.p->m_bits & TreeNode::T_NEED_REPORT_BATCH_COMPLETED)
+ {
+ jam();
+ ndbrequire(childPtr.p->m_info != 0 &&
+ childPtr.p->m_info->m_parent_batch_complete !=0 );
+ (this->*(childPtr.p->m_info->m_parent_batch_complete))(signal,
+ requestPtr,
+ childPtr);
+ }
+ }
+}
+
+void
+Dbspj::abort(Signal* signal, Ptr<Request> requestPtr, Uint32 errCode)
+{
+ jam();
+
+ if ((requestPtr.p->m_state & Request::RS_ABORTING) != 0)
+ {
+ jam();
+ return;
+ }
+
+ requestPtr.p->m_state |= Request::RS_ABORTING;
+ requestPtr.p->m_errCode = errCode;
+
+ {
+ Ptr<TreeNode> nodePtr;
+ Local_TreeNode_list list(m_treenode_pool, requestPtr.p->m_nodes);
+ for (list.first(nodePtr); !nodePtr.isNull(); list.next(nodePtr))
+ {
+ jam();
+ /**
+ * clear T_REPORT_BATCH_COMPLETE so that child nodes don't get confused
+ * during abort
+ */
+ nodePtr.p->m_bits &= ~Uint32(TreeNode::T_REPORT_BATCH_COMPLETE);
+
+ ndbrequire(nodePtr.p->m_info != 0);
+ if (nodePtr.p->m_info->m_abort != 0)
+ {
+ jam();
+ (this->*(nodePtr.p->m_info->m_abort))(signal, requestPtr, nodePtr);
+ }
+ }
+ }
+
+ checkBatchComplete(signal, requestPtr, 0);
+}
+
+Uint32
+Dbspj::nodeFail(Signal* signal, Ptr<Request> requestPtr,
+ NdbNodeBitmask nodes)
+{
+ Uint32 cnt = 0;
+ Uint32 iter = 0;
+ Uint32 outstanding = requestPtr.p->m_outstanding;
+ Uint32 aborting = requestPtr.p->m_state & Request::RS_ABORTING;
+
+ {
+ Ptr<TreeNode> nodePtr;
+ Local_TreeNode_list list(m_treenode_pool, requestPtr.p->m_nodes);
+ for (list.first(nodePtr); !nodePtr.isNull(); list.next(nodePtr))
+ {
+ jam();
+ ndbrequire(nodePtr.p->m_info != 0);
+ if (nodePtr.p->m_info->m_execNODE_FAILREP != 0)
+ {
+ jam();
+ iter ++;
+ cnt += (this->*(nodePtr.p->m_info->m_execNODE_FAILREP))(signal,
+ requestPtr,
+ nodePtr, nodes);
+ }
+ }
+ }
+
+ if (cnt == 0)
+ {
+ jam();
+ /**
+ * None of the operations needed NodeFailRep "action"
+ * check if our TC has died...but...only needed in
+ * scan case...for lookup...not so...
+ */
+ if (requestPtr.p->isScan() &&
+ nodes.get(refToNode(requestPtr.p->m_senderRef)))
+ {
+ jam();
+ abort(signal, requestPtr, DbspjErr::NodeFailure);
+ }
+ }
+ else
+ {
+ jam();
+ abort(signal, requestPtr, DbspjErr::NodeFailure);
+
+ if (aborting && outstanding && requestPtr.p->m_outstanding == 0)
+ {
+ jam();
+ checkBatchComplete(signal, requestPtr, 0);
+ }
+ }
+
+ return cnt + iter;
+}
+
+void
+Dbspj::complete(Signal* signal, Ptr<Request> requestPtr)
+{
+ /**
+ * we need to run complete-phase before sending last SCAN_FRAGCONF
+ */
+ Uint32 flags = requestPtr.p->m_state &
+ (Request::RS_ABORTING | Request::RS_WAITING);
+
+ requestPtr.p->m_state = Request::RS_COMPLETING | flags;
+
+ // clear bit so that next batchComplete()
+ // will continue to cleanup
+ ndbassert((requestPtr.p->m_bits & Request::RT_NEED_COMPLETE) != 0);
+ requestPtr.p->m_bits &= ~(Uint32)Request::RT_NEED_COMPLETE;
+ requestPtr.p->m_outstanding = 0;
+ {
+ Ptr<TreeNode> nodePtr;
+ Local_TreeNode_list list(m_treenode_pool, requestPtr.p->m_nodes);
+ for (list.first(nodePtr); !nodePtr.isNull(); list.next(nodePtr))
+ {
+ jam();
+ ndbrequire(nodePtr.p->m_info != 0);
+ if (nodePtr.p->m_info->m_complete != 0)
+ {
+ jam();
+ (this->*(nodePtr.p->m_info->m_complete))(signal, requestPtr, nodePtr);
+ }
+ }
+
+ /**
+ * preferably RT_NEED_COMPLETE should only be set if blocking
+ * calls are used, in which case m_outstanding should have been increased
+ *
+ * BUT: scanIndex does DIH_SCAN_TAB_COMPLETE_REP which does not send reply
+ * so it not really "blocking"
+ * i.e remove assert
+ */
+ //ndbassert(requestPtr.p->m_outstanding);
+ }
+ checkBatchComplete(signal, requestPtr, 0);
+}
+
+void
+Dbspj::cleanup(Ptr<Request> requestPtr)
+{
+ ndbrequire(requestPtr.p->m_active_nodes.isclear());
+ {
+ Ptr<TreeNode> nodePtr;
+ Local_TreeNode_list list(m_treenode_pool, requestPtr.p->m_nodes);
+ for (list.first(nodePtr); !nodePtr.isNull(); )
+ {
+ jam();
+ ndbrequire(nodePtr.p->m_info != 0 && nodePtr.p->m_info->m_cleanup != 0);
+ (this->*(nodePtr.p->m_info->m_cleanup))(requestPtr, nodePtr);
+
+ Ptr<TreeNode> tmp = nodePtr;
+ list.next(nodePtr);
+ m_treenode_pool.release(tmp);
+ }
+ list.remove();
+ }
+ if (requestPtr.p->isScan())
+ {
+ jam();
+
+ if (unlikely((requestPtr.p->m_state & Request::RS_WAITING) != 0))
+ {
+ jam();
+ requestPtr.p->m_state = Request::RS_ABORTED;
+ return;
+ }
+
+#ifdef VM_TRACE
+ {
+ Request key;
+ key.m_transId[0] = requestPtr.p->m_transId[0];
+ key.m_transId[1] = requestPtr.p->m_transId[1];
+ key.m_senderData = requestPtr.p->m_senderData;
+ Ptr<Request> tmp;
+ ndbrequire(m_scan_request_hash.find(tmp, key));
+ }
+#endif
+ m_scan_request_hash.remove(requestPtr);
+ }
+ else
+ {
+ jam();
+#ifdef VM_TRACE
+ {
+ Request key;
+ key.m_transId[0] = requestPtr.p->m_transId[0];
+ key.m_transId[1] = requestPtr.p->m_transId[1];
+ key.m_senderData = requestPtr.p->m_senderData;
+ Ptr<Request> tmp;
+ ndbrequire(m_lookup_request_hash.find(tmp, key));
+ }
+#endif
+ m_lookup_request_hash.remove(requestPtr);
+ }
+ releaseRequestBuffers(requestPtr, false);
+ ArenaHead ah = requestPtr.p->m_arena;
+ m_request_pool.release(requestPtr);
+ m_arenaAllocator.release(ah);
+}
+
+void
+Dbspj::cleanup_common(Ptr<Request> requestPtr, Ptr<TreeNode> treeNodePtr)
+{
+ jam();
+
+ LocalArenaPoolImpl pool(requestPtr.p->m_arena, m_dependency_map_pool);
+ {
+ Local_dependency_map list(pool, treeNodePtr.p->m_dependent_nodes);
+ list.release();
+ }
+
+ {
+ Local_pattern_store pattern(pool, treeNodePtr.p->m_keyPattern);
+ pattern.release();
+ }
+
+ {
+ Local_pattern_store pattern(pool, treeNodePtr.p->m_attrParamPattern);
+ pattern.release();
+ }
+
+ if (treeNodePtr.p->m_send.m_keyInfoPtrI != RNIL)
+ {
+ jam();
+ releaseSection(treeNodePtr.p->m_send.m_keyInfoPtrI);
+ }
+
+ if (treeNodePtr.p->m_send.m_attrInfoPtrI != RNIL)
+ {
+ jam();
+ releaseSection(treeNodePtr.p->m_send.m_attrInfoPtrI);
+ }
+}
+
+/**
+ * Processing of signals from LQH
+ */
+void
+Dbspj::execLQHKEYREF(Signal* signal)
+{
+ jamEntry();
+
+ const LqhKeyRef* ref = reinterpret_cast<const LqhKeyRef*>(signal->getDataPtr());
+
+ DEBUG("execLQHKEYREF, errorCode:" << ref->errorCode);
+ Ptr<TreeNode> treeNodePtr;
+ m_treenode_pool.getPtr(treeNodePtr, ref->connectPtr);
+
+ Ptr<Request> requestPtr;
+ m_request_pool.getPtr(requestPtr, treeNodePtr.p->m_requestPtrI);
+
+ ndbrequire(treeNodePtr.p->m_info && treeNodePtr.p->m_info->m_execLQHKEYREF);
+ (this->*(treeNodePtr.p->m_info->m_execLQHKEYREF))(signal,
+ requestPtr,
+ treeNodePtr);
+}
+
+void
+Dbspj::execLQHKEYCONF(Signal* signal)
+{
+ jamEntry();
+
+ DEBUG("execLQHKEYCONF");
+
+ const LqhKeyConf* conf = reinterpret_cast<const LqhKeyConf*>(signal->getDataPtr());
+ Ptr<TreeNode> treeNodePtr;
+ m_treenode_pool.getPtr(treeNodePtr, conf->opPtr);
+
+ Ptr<Request> requestPtr;
+ m_request_pool.getPtr(requestPtr, treeNodePtr.p->m_requestPtrI);
+
+ ndbrequire(treeNodePtr.p->m_info && treeNodePtr.p->m_info->m_execLQHKEYCONF);
+ (this->*(treeNodePtr.p->m_info->m_execLQHKEYCONF))(signal,
+ requestPtr,
+ treeNodePtr);
+}
+
+void
+Dbspj::execSCAN_FRAGREF(Signal* signal)
+{
+ jamEntry();
+ const ScanFragRef* ref = reinterpret_cast<const ScanFragRef*>(signal->getDataPtr());
+
+ DEBUG("execSCAN_FRAGREF, errorCode:" << ref->errorCode);
+
+ Ptr<ScanFragHandle> scanFragHandlePtr;
+ m_scanfraghandle_pool.getPtr(scanFragHandlePtr, ref->senderData);
+ Ptr<TreeNode> treeNodePtr;
+ m_treenode_pool.getPtr(treeNodePtr, scanFragHandlePtr.p->m_treeNodePtrI);
+ Ptr<Request> requestPtr;
+ m_request_pool.getPtr(requestPtr, treeNodePtr.p->m_requestPtrI);
+
+ ndbrequire(treeNodePtr.p->m_info&&treeNodePtr.p->m_info->m_execSCAN_FRAGREF);
+ (this->*(treeNodePtr.p->m_info->m_execSCAN_FRAGREF))(signal,
+ requestPtr,
+ treeNodePtr,
+ scanFragHandlePtr);
+}
+
+void
+Dbspj::execSCAN_HBREP(Signal* signal)
+{
+ jamEntry();
+
+ Uint32 senderData = signal->theData[0];
+ //Uint32 transId[2] = { signal->theData[1], signal->theData[2] };
+
+ Ptr<ScanFragHandle> scanFragHandlePtr;
+ m_scanfraghandle_pool.getPtr(scanFragHandlePtr, senderData);
+ Ptr<TreeNode> treeNodePtr;
+ m_treenode_pool.getPtr(treeNodePtr, scanFragHandlePtr.p->m_treeNodePtrI);
+ Ptr<Request> requestPtr;
+ m_request_pool.getPtr(requestPtr, treeNodePtr.p->m_requestPtrI);
+
+ Uint32 ref = requestPtr.p->m_senderRef;
+ signal->theData[0] = requestPtr.p->m_senderData;
+ sendSignal(ref, GSN_SCAN_HBREP, signal, 3, JBB);
+}
+
+void
+Dbspj::execSCAN_FRAGCONF(Signal* signal)
+{
+ jamEntry();
+ DEBUG("execSCAN_FRAGCONF");
+
+ const ScanFragConf* conf = reinterpret_cast<const ScanFragConf*>(signal->getDataPtr());
+
+#ifdef DEBUG_SCAN_FRAGREQ
+ ndbout_c("Dbspj::execSCAN_FRAGCONF() receiveing SCAN_FRAGCONF ");
+ printSCAN_FRAGCONF(stdout, signal->getDataPtrSend(),
+ conf->total_len,
+ DBLQH);
+#endif
+
+ Ptr<ScanFragHandle> scanFragHandlePtr;
+ m_scanfraghandle_pool.getPtr(scanFragHandlePtr, conf->senderData);
+ Ptr<TreeNode> treeNodePtr;
+ m_treenode_pool.getPtr(treeNodePtr, scanFragHandlePtr.p->m_treeNodePtrI);
+ Ptr<Request> requestPtr;
+ m_request_pool.getPtr(requestPtr, treeNodePtr.p->m_requestPtrI);
+
+ ndbrequire(treeNodePtr.p->m_info&&treeNodePtr.p->m_info->m_execSCAN_FRAGCONF);
+ (this->*(treeNodePtr.p->m_info->m_execSCAN_FRAGCONF))(signal,
+ requestPtr,
+ treeNodePtr,
+ scanFragHandlePtr);
+}
+
+void
+Dbspj::execSCAN_NEXTREQ(Signal* signal)
+{
+ jamEntry();
+ const ScanFragNextReq * req = (ScanFragNextReq*)&signal->theData[0];
+
+ DEBUG("Incomming SCAN_NEXTREQ");
+#ifdef DEBUG_SCAN_FRAGREQ
+ printSCANFRAGNEXTREQ(stdout, &signal->theData[0],
+ ScanFragNextReq::SignalLength, DBLQH);
+#endif
+
+ Request key;
+ key.m_transId[0] = req->transId1;
+ key.m_transId[1] = req->transId2;
+ key.m_senderData = req->senderData;
+
+ Ptr<Request> requestPtr;
+ if (unlikely(!m_scan_request_hash.find(requestPtr, key)))
+ {
+ jam();
+ ndbrequire(req->requestInfo == ScanFragNextReq::ZCLOSE);
+ return;
+ }
+
+#ifdef SPJ_TRACE_TIME
+ Uint64 now = spj_now();
+ Uint64 then = requestPtr.p->m_save_time;
+ requestPtr.p->m_sum_waiting += Uint32(now - then);
+ requestPtr.p->m_save_time = now;
+#endif
+
+ Uint32 state = requestPtr.p->m_state;
+ requestPtr.p->m_state = state & ~Uint32(Request::RS_WAITING);
+
+ if (unlikely(state == Request::RS_ABORTED))
+ {
+ jam();
+ batchComplete(signal, requestPtr);
+ return;
+ }
+
+ if (unlikely((state & Request::RS_ABORTING) != 0))
+ {
+ jam();
+ /**
+ * abort is already in progress...
+ * since RS_WAITING is cleared...it will end this request
+ */
+ return;
+ }
+
+ if (req->requestInfo == ScanFragNextReq::ZCLOSE) // Requested close scan
+ {
+ jam();
+ abort(signal, requestPtr, 0);
+ return;
+ }
+
+ ndbrequire((state & Request::RS_WAITING) != 0);
+ ndbrequire(requestPtr.p->m_outstanding == 0);
+
+ {
+ /**
+ * Scroll all relevant cursors...
+ */
+ Ptr<TreeNode> treeNodePtr;
+ Local_TreeNodeCursor_list list(m_treenode_pool,
+ requestPtr.p->m_cursor_nodes);
+ for (list.first(treeNodePtr); !treeNodePtr.isNull(); list.next(treeNodePtr))
+ {
+ jam();
+ ndbrequire(treeNodePtr.p->m_state == TreeNode::TN_ACTIVE);
+ ndbrequire(treeNodePtr.p->m_info != 0 &&
+ treeNodePtr.p->m_info->m_execSCAN_NEXTREQ != 0);
+ (this->*(treeNodePtr.p->m_info->m_execSCAN_NEXTREQ))(signal,
+ requestPtr,
+ treeNodePtr);
+ }
+ }
+}
+
+void
+Dbspj::execTRANSID_AI(Signal* signal)
+{
+ jamEntry();
+ DEBUG("execTRANSID_AI");
+ TransIdAI * req = (TransIdAI *)signal->getDataPtr();
+ Uint32 ptrI = req->connectPtr;
+ //Uint32 transId[2] = { req->transId[0], req->transId[1] };
+
+ Ptr<TreeNode> treeNodePtr;
+ m_treenode_pool.getPtr(treeNodePtr, ptrI);
+ Ptr<Request> requestPtr;
+ m_request_pool.getPtr(requestPtr, treeNodePtr.p->m_requestPtrI);
+
+ ndbrequire(signal->getNoOfSections() != 0); // TODO check if this can happen
+
+ SegmentedSectionPtr dataPtr;
+ {
+ SectionHandle handle(this, signal);
+ handle.getSection(dataPtr, 0);
+ handle.clear();
+ }
+
+#if defined(DEBUG_LQHKEYREQ) || defined(DEBUG_SCAN_FRAGREQ)
+ printf("execTRANSID_AI: ");
+ print(dataPtr, stdout);
+#endif
+
+ /**
+ * build easy-access-array for row
+ */
+ Uint32 tmp[2+MAX_ATTRIBUTES_IN_TABLE];
+ RowPtr::Header* header = CAST_PTR(RowPtr::Header, &tmp[0]);
+
+ Uint32 cnt = buildRowHeader(header, dataPtr);
+ ndbassert(header->m_len < NDB_ARRAY_SIZE(tmp));
+
+ struct RowPtr row;
+ row.m_type = RowPtr::RT_SECTION;
+ row.m_src_node_ptrI = treeNodePtr.i;
+ row.m_row_data.m_section.m_header = header;
+ row.m_row_data.m_section.m_dataPtr.assign(dataPtr);
+
+ getCorrelationData(row.m_row_data.m_section,
+ cnt - 1,
+ row.m_src_correlation);
+
+ if (treeNodePtr.p->m_bits & TreeNode::T_ROW_BUFFER)
+ {
+ jam();
+ Uint32 err = storeRow(requestPtr, treeNodePtr, row);
+ ndbrequire(err == 0);
+ }
+
+ ndbrequire(treeNodePtr.p->m_info&&treeNodePtr.p->m_info->m_execTRANSID_AI);
+
+ (this->*(treeNodePtr.p->m_info->m_execTRANSID_AI))(signal,
+ requestPtr,
+ treeNodePtr,
+ row);
+ release(dataPtr);
+}
+
+Uint32
+Dbspj::storeRow(Ptr<Request> requestPtr, Ptr<TreeNode> treeNodePtr, RowPtr &row)
+{
+ ndbassert(row.m_type == RowPtr::RT_SECTION);
+ SegmentedSectionPtr dataPtr = row.m_row_data.m_section.m_dataPtr;
+ Uint32 * headptr = (Uint32*)row.m_row_data.m_section.m_header;
+ Uint32 headlen = 1 + row.m_row_data.m_section.m_header->m_len;
+
+ /**
+ * If rows are not in map, then they are kept in linked list
+ */
+ Uint32 linklen = (treeNodePtr.p->m_bits & TreeNode::T_ROW_BUFFER_MAP)?
+ 0 : 2;
+
+ Uint32 totlen = 0;
+ totlen += dataPtr.sz;
+ totlen += headlen;
+ totlen += linklen;
+
+ RowRef ref;
+ Uint32 * dstptr = 0;
+ if ((requestPtr.p->m_bits & Request::RT_VAR_ALLOC) == 0)
+ {
+ jam();
+ dstptr = stackAlloc(requestPtr.p->m_rowBuffer, ref, totlen);
+ }
+ else
+ {
+ jam();
+ dstptr = varAlloc(requestPtr.p->m_rowBuffer, ref, totlen);
+ }
+
+ if (unlikely(dstptr == 0))
+ {
+ jam();
+ return DbspjErr::OutOfRowMemory;
+ }
+
+ row.m_type = RowPtr::RT_LINEAR;
+ row.m_row_data.m_linear.m_row_ref = ref;
+ row.m_row_data.m_linear.m_header = (RowPtr::Header*)(dstptr + linklen);
+ row.m_row_data.m_linear.m_data = dstptr + linklen + headlen;
+
+ memcpy(dstptr + linklen, headptr, 4 * headlen);
+ copy(dstptr + linklen + headlen, dataPtr);
+
+ if (linklen)
+ {
+ jam();
+ NullRowRef.copyto_link(dstptr); // Null terminate list...
+ add_to_list(treeNodePtr.p->m_row_list, ref);
+ }
+ else
+ {
+ jam();
+ return add_to_map(requestPtr, treeNodePtr, row.m_src_correlation, ref);
+ }
+
+ return 0;
+}
+
+void
+Dbspj::setupRowPtr(Ptr<TreeNode> treeNodePtr,
+ RowPtr& row, RowRef ref, const Uint32 * src)
+{
+ Uint32 linklen = (treeNodePtr.p->m_bits & TreeNode::T_ROW_BUFFER_MAP)?
+ 0 : 2;
+ const RowPtr::Header * headptr = (RowPtr::Header*)(src + linklen);
+ Uint32 headlen = 1 + headptr->m_len;
+
+ row.m_type = RowPtr::RT_LINEAR;
+ row.m_row_data.m_linear.m_row_ref = ref;
+ row.m_row_data.m_linear.m_header = headptr;
+ row.m_row_data.m_linear.m_data = (Uint32*)headptr + headlen;
+}
+
+void
+Dbspj::add_to_list(SLFifoRowList & list, RowRef rowref)
+{
+ if (list.isNull())
+ {
+ jam();
+ list.m_first_row_page_id = rowref.m_page_id;
+ list.m_first_row_page_pos = rowref.m_page_pos;
+ }
+ else
+ {
+ jam();
+ /**
+ * add last to list
+ */
+ RowRef last;
+ last.m_allocator = rowref.m_allocator;
+ last.m_page_id = list.m_last_row_page_id;
+ last.m_page_pos = list.m_last_row_page_pos;
+ Uint32 * rowptr;
+ if (rowref.m_allocator == 0)
+ {
+ jam();
+ rowptr = get_row_ptr_stack(last);
+ }
+ else
+ {
+ jam();
+ rowptr = get_row_ptr_var(last);
+ }
+ rowref.copyto_link(rowptr);
+ }
+
+ list.m_last_row_page_id = rowref.m_page_id;
+ list.m_last_row_page_pos = rowref.m_page_pos;
+}
+
+Uint32 *
+Dbspj::get_row_ptr_stack(RowRef pos)
+{
+ ndbassert(pos.m_allocator == 0);
+ Ptr<RowPage> ptr;
+ m_page_pool.getPtr(ptr, pos.m_page_id);
+ return ptr.p->m_data + pos.m_page_pos;
+}
+
+Uint32 *
+Dbspj::get_row_ptr_var(RowRef pos)
+{
+ ndbassert(pos.m_allocator == 1);
+ Ptr<RowPage> ptr;
+ m_page_pool.getPtr(ptr, pos.m_page_id);
+ return ((Var_page*)ptr.p)->get_ptr(pos.m_page_pos);
+}
+
+bool
+Dbspj::first(Ptr<Request> requestPtr, Ptr<TreeNode> treeNodePtr,
+ SLFifoRowListIterator& iter)
+{
+ Uint32 var = (requestPtr.p->m_bits & Request::RT_VAR_ALLOC) != 0;
+ SLFifoRowList & list = treeNodePtr.p->m_row_list;
+ if (list.isNull())
+ {
+ jam();
+ iter.setNull();
+ return false;
+ }
+
+ iter.m_ref.m_allocator = var;
+ iter.m_ref.m_page_id = list.m_first_row_page_id;
+ iter.m_ref.m_page_pos = list.m_first_row_page_pos;
+ if (var == 0)
+ {
+ jam();
+ iter.m_row_ptr = get_row_ptr_stack(iter.m_ref);
+ }
+ else
+ {
+ jam();
+ iter.m_row_ptr = get_row_ptr_var(iter.m_ref);
+ }
+
+ return true;
+}
+
+bool
+Dbspj::next(SLFifoRowListIterator& iter)
+{
+ iter.m_ref.assign_from_link(iter.m_row_ptr);
+ if (iter.m_ref.isNull())
+ {
+ jam();
+ return false;
+ }
+
+ if (iter.m_ref.m_allocator == 0)
+ {
+ jam();
+ iter.m_row_ptr = get_row_ptr_stack(iter.m_ref);
+ }
+ else
+ {
+ jam();
+ iter.m_row_ptr = get_row_ptr_var(iter.m_ref);
+ }
+ return true;
+}
+
+bool
+Dbspj::next(Ptr<Request> requestPtr, Ptr<TreeNode> treeNodePtr,
+ SLFifoRowListIterator& iter, SLFifoRowListIteratorPtr start)
+{
+ Uint32 var = (requestPtr.p->m_bits & Request::RT_VAR_ALLOC) != 0;
+ (void)var;
+ ndbassert(var == iter.m_ref.m_allocator);
+ if (iter.m_ref.m_allocator == 0)
+ {
+ jam();
+ iter.m_row_ptr = get_row_ptr_stack(start.m_ref);
+ }
+ else
+ {
+ jam();
+ iter.m_row_ptr = get_row_ptr_var(start.m_ref);
+ }
+ return next(iter);
+}
+
+Uint32
+Dbspj::add_to_map(Ptr<Request> requestPtr, Ptr<TreeNode> treeNodePtr,
+ Uint32 corrVal, RowRef rowref)
+{
+ Uint32 * mapptr;
+ RowMap& map = treeNodePtr.p->m_row_map;
+ if (map.isNull())
+ {
+ jam();
+ Uint16 batchsize = treeNodePtr.p->m_batch_size;
+ Uint32 sz16 = RowMap::MAP_SIZE_PER_REF_16 * batchsize;
+ Uint32 sz32 = (sz16 + 1) / 2;
+ RowRef ref;
+ if ((requestPtr.p->m_bits & Request::RT_VAR_ALLOC) == 0)
+ {
+ jam();
+ mapptr = stackAlloc(requestPtr.p->m_rowBuffer, ref, sz32);
+ }
+ else
+ {
+ jam();
+ mapptr = varAlloc(requestPtr.p->m_rowBuffer, ref, sz32);
+ }
+ if (unlikely(mapptr == 0))
+ {
+ jam();
+ return DbspjErr::OutOfRowMemory;
+ }
+ map.assign(ref);
+ map.m_elements = 0;
+ map.m_size = batchsize;
+ map.clear(mapptr);
+ }
+ else
+ {
+ jam();
+ RowRef ref;
+ map.copyto(ref);
+ if (ref.m_allocator == 0)
+ {
+ jam();
+ mapptr = get_row_ptr_stack(ref);
+ }
+ else
+ {
+ jam();
+ mapptr = get_row_ptr_var(ref);
+ }
+ }
+
+ Uint32 pos = corrVal & 0xFFFF;
+ ndbrequire(pos < map.m_size);
+ ndbrequire(map.m_elements < map.m_size);
+
+ if (1)
+ {
+ /**
+ * Check that *pos* is empty
+ */
+ RowRef check;
+ map.load(mapptr, pos, check);
+ ndbrequire(check.m_page_pos == 0xFFFF);
+ }
+
+ map.store(mapptr, pos, rowref);
+
+ return 0;
+}
+
+bool
+Dbspj::first(Ptr<Request> requestPtr, Ptr<TreeNode> treeNodePtr,
+ RowMapIterator & iter)
+{
+ Uint32 var = (requestPtr.p->m_bits & Request::RT_VAR_ALLOC) != 0;
+ RowMap& map = treeNodePtr.p->m_row_map;
+ if (map.isNull())
+ {
+ jam();
+ iter.setNull();
+ return false;
+ }
+
+ if (var == 0)
+ {
+ jam();
+ iter.m_map_ptr = get_row_ptr_stack(map.m_map_ref);
+ }
+ else
+ {
+ jam();
+ iter.m_map_ptr = get_row_ptr_var(map.m_map_ref);
+ }
+ iter.m_size = map.m_size;
+ iter.m_ref.m_allocator = var;
+
+ Uint32 pos = 0;
+ while (RowMap::isNull(iter.m_map_ptr, pos) && pos < iter.m_size)
+ pos++;
+
+ if (pos == iter.m_size)
+ {
+ jam();
+ iter.setNull();
+ return false;
+ }
+ else
+ {
+ jam();
+ RowMap::load(iter.m_map_ptr, pos, iter.m_ref);
+ iter.m_element_no = pos;
+ if (var == 0)
+ {
+ jam();
+ iter.m_row_ptr = get_row_ptr_stack(iter.m_ref);
+ }
+ else
+ {
+ jam();
+ iter.m_row_ptr = get_row_ptr_var(iter.m_ref);
+ }
+ return true;
+ }
+}
+
+bool
+Dbspj::next(RowMapIterator & iter)
+{
+ Uint32 pos = iter.m_element_no + 1;
+ while (RowMap::isNull(iter.m_map_ptr, pos) && pos < iter.m_size)
+ pos++;
+
+ if (pos == iter.m_size)
+ {
+ jam();
+ iter.setNull();
+ return false;
+ }
+ else
+ {
+ jam();
+ RowMap::load(iter.m_map_ptr, pos, iter.m_ref);
+ iter.m_element_no = pos;
+ if (iter.m_ref.m_allocator == 0)
+ {
+ jam();
+ iter.m_row_ptr = get_row_ptr_stack(iter.m_ref);
+ }
+ else
+ {
+ jam();
+ iter.m_row_ptr = get_row_ptr_var(iter.m_ref);
+ }
+ return true;
+ }
+}
+
+bool
+Dbspj::next(Ptr<Request> requestPtr, Ptr<TreeNode> treeNodePtr,
+ RowMapIterator & iter, RowMapIteratorPtr start)
+{
+ Uint32 var = (requestPtr.p->m_bits & Request::RT_VAR_ALLOC) != 0;
+ RowMap& map = treeNodePtr.p->m_row_map;
+ ndbrequire(!map.isNull());
+
+ if (var == 0)
+ {
+ jam();
+ iter.m_map_ptr = get_row_ptr_stack(map.m_map_ref);
+ }
+ else
+ {
+ jam();
+ iter.m_map_ptr = get_row_ptr_var(map.m_map_ref);
+ }
+ iter.m_size = map.m_size;
+
+ RowMap::load(iter.m_map_ptr, start.m_element_no, iter.m_ref);
+ iter.m_element_no = start.m_element_no;
+ return next(iter);
+}
+
+Uint32 *
+Dbspj::stackAlloc(RowBuffer & buffer, RowRef& dst, Uint32 sz)
+{
+ Ptr<RowPage> ptr;
+ LocalDLFifoList<RowPage> list(m_page_pool, buffer.m_page_list);
+
+ Uint32 pos = buffer.m_stack.m_pos;
+ const Uint32 SIZE = RowPage::SIZE;
+ if (list.isEmpty() || (pos + sz) > SIZE)
+ {
+ jam();
+ bool ret = allocPage(ptr);
+ if (unlikely(ret == false))
+ {
+ jam();
+ return 0;
+ }
+
+ pos = 0;
+ list.addLast(ptr);
+ }
+ else
+ {
+ list.last(ptr);
+ }
+
+ dst.m_page_id = ptr.i;
+ dst.m_page_pos = pos;
+ dst.m_allocator = 0;
+ buffer.m_stack.m_pos = pos + sz;
+ return ptr.p->m_data + pos;
+}
+
+Uint32 *
+Dbspj::varAlloc(RowBuffer & buffer, RowRef& dst, Uint32 sz)
+{
+ Ptr<RowPage> ptr;
+ LocalDLFifoList<RowPage> list(m_page_pool, buffer.m_page_list);
+
+ Uint32 free_space = buffer.m_var.m_free;
+ if (list.isEmpty() || free_space < (sz + 1))
+ {
+ jam();
+ bool ret = allocPage(ptr);
+ if (unlikely(ret == false))
+ {
+ jam();
+ return 0;
+ }
+
+ list.addLast(ptr);
+ ((Var_page*)ptr.p)->init();
+ }
+ else
+ {
+ jam();
+ list.last(ptr);
+ }
+
+ Var_page * vp = (Var_page*)ptr.p;
+ Uint32 pos = vp->alloc_record(sz, (Var_page*)m_buffer0, Var_page::CHAIN);
+
+ dst.m_page_id = ptr.i;
+ dst.m_page_pos = pos;
+ dst.m_allocator = 1;
+ buffer.m_var.m_free = vp->free_space;
+ return vp->get_ptr(pos);
+}
+
+bool
+Dbspj::allocPage(Ptr<RowPage> & ptr)
+{
+ if (m_free_page_list.firstItem == RNIL)
+ {
+ jam();
+ ptr.p = (RowPage*)m_ctx.m_mm.alloc_page(RT_SPJ_DATABUFFER,
+ &ptr.i,
+ Ndbd_mem_manager::NDB_ZONE_ANY);
+ if (ptr.p == 0)
+ {
+ return false;
+ }
+ return true;
+ }
+ else
+ {
+ jam();
+ LocalSLList<RowPage> list(m_page_pool, m_free_page_list);
+ bool ret = list.remove_front(ptr);
+ ndbrequire(ret);
+ return ret;
+ }
+}
+
+void
+Dbspj::releasePage(Ptr<RowPage> ptr)
+{
+ LocalSLList<RowPage> list(m_page_pool, m_free_page_list);
+ list.add(ptr);
+}
+
+void
+Dbspj::releasePages(Uint32 first, Ptr<RowPage> last)
+{
+ LocalSLList<RowPage> list(m_page_pool, m_free_page_list);
+ list.add(first, last);
+}
+
+void
+Dbspj::releaseGlobal(Signal * signal)
+{
+ Uint32 delay = 100;
+ LocalSLList<RowPage> list(m_page_pool, m_free_page_list);
+ if (list.empty())
+ {
+ jam();
+ delay = 300;
+ }
+ else
+ {
+ Ptr<RowPage> ptr;
+ list.remove_front(ptr);
+ m_ctx.m_mm.release_page(RT_SPJ_DATABUFFER, ptr.i);
+ }
+
+ signal->theData[0] = 0;
+ sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, delay, 1);
+}
+
+/**
+ * END - MODULE GENERIC
+ */
+
+/**
+ * MODULE LOOKUP
+ */
+const Dbspj::OpInfo
+Dbspj::g_LookupOpInfo =
+{
+ &Dbspj::lookup_build,
+ 0, // prepare
+ &Dbspj::lookup_start,
+ &Dbspj::lookup_execTRANSID_AI,
+ &Dbspj::lookup_execLQHKEYREF,
+ &Dbspj::lookup_execLQHKEYCONF,
+ 0, // execSCAN_FRAGREF
+ 0, // execSCAN_FRAGCONF
+ &Dbspj::lookup_parent_row,
+ &Dbspj::lookup_parent_batch_complete,
+ 0, // Dbspj::lookup_execSCAN_NEXTREQ
+ 0, // Dbspj::lookup_complete
+ &Dbspj::lookup_abort,
+ &Dbspj::lookup_execNODE_FAILREP,
+ &Dbspj::lookup_cleanup
+};
+
+Uint32
+Dbspj::lookup_build(Build_context& ctx,
+ Ptr<Request> requestPtr,
+ const QueryNode* qn,
+ const QueryNodeParameters* qp)
+{
+ Uint32 err = 0;
+ Ptr<TreeNode> treeNodePtr;
+ const QN_LookupNode * node = (const QN_LookupNode*)qn;
+ const QN_LookupParameters * param = (const QN_LookupParameters*)qp;
+ do
+ {
+ err = createNode(ctx, requestPtr, treeNodePtr);
+ if (unlikely(err != 0))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+
+ treeNodePtr.p->m_info = &g_LookupOpInfo;
+ Uint32 transId1 = requestPtr.p->m_transId[0];
+ Uint32 transId2 = requestPtr.p->m_transId[1];
+ Uint32 savePointId = ctx.m_savepointId;
+
+ Uint32 treeBits = node->requestInfo;
+ Uint32 paramBits = param->requestInfo;
+ //ndbout_c("Dbspj::lookup_build() treeBits=%.8x paramBits=%.8x",
+ // treeBits, paramBits);
+ LqhKeyReq* dst = (LqhKeyReq*)treeNodePtr.p->m_lookup_data.m_lqhKeyReq;
+ {
+ /**
+ * static variables
+ */
+ dst->tcBlockref = reference();
+ dst->clientConnectPtr = treeNodePtr.i;
+
+ /**
+ * TODO reference()+treeNodePtr.i is passed twice
+ * this can likely be optimized using the requestInfo-bits
+ * UPDATE: This can be accomplished by *not* setApplicationAddressFlag
+ * and patch LQH to then instead use tcBlockref/clientConnectPtr
+ */
+ dst->transId1 = transId1;
+ dst->transId2 = transId2;
+ dst->savePointId = savePointId;
+ dst->scanInfo = 0;
+ dst->attrLen = 0;
+ /** Initialy set reply ref to client, do_send will set SPJ refs if non-LEAF */
+ dst->variableData[0] = ctx.m_resultRef;
+ dst->variableData[1] = param->resultData;
+ Uint32 requestInfo = 0;
+ LqhKeyReq::setOperation(requestInfo, ZREAD);
+ LqhKeyReq::setApplicationAddressFlag(requestInfo, 1);
+ LqhKeyReq::setDirtyFlag(requestInfo, 1);
+ LqhKeyReq::setSimpleFlag(requestInfo, 1);
+ LqhKeyReq::setNormalProtocolFlag(requestInfo, 0); // Assume T_LEAF
+ LqhKeyReq::setCorrFactorFlag(requestInfo, 1);
+ LqhKeyReq::setNoDiskFlag(requestInfo,
+ (treeBits & DABits::NI_LINKED_DISK) == 0 &&
+ (paramBits & DABits::PI_DISK_ATTR) == 0);
+ dst->requestInfo = requestInfo;
+ }
+
+ err = DbspjErr::InvalidTreeNodeSpecification;
+ if (unlikely(node->len < QN_LookupNode::NodeSize))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+
+ if (treeBits & QN_LookupNode::L_UNIQUE_INDEX)
+ {
+ jam();
+ treeNodePtr.p->m_bits |= TreeNode::T_UNIQUE_INDEX_LOOKUP;
+ }
+
+ Uint32 tableId = node->tableId;
+ Uint32 schemaVersion = node->tableVersion;
+
+ Uint32 tableSchemaVersion = tableId + ((schemaVersion << 16) & 0xFFFF0000);
+ dst->tableSchemaVersion = tableSchemaVersion;
+
+ err = DbspjErr::InvalidTreeParametersSpecification;
+ DEBUG("param len: " << param->len);
+ if (unlikely(param->len < QN_LookupParameters::NodeSize))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+
+ ctx.m_resultData = param->resultData;
+ treeNodePtr.p->m_lookup_data.m_api_resultRef = ctx.m_resultRef;
+ treeNodePtr.p->m_lookup_data.m_api_resultData = param->resultData;
+ treeNodePtr.p->m_lookup_data.m_outstanding = 0;
+ treeNodePtr.p->m_lookup_data.m_parent_batch_complete = false;
+
+ /**
+ * Parse stuff common lookup/scan-frag
+ */
+ struct DABuffer nodeDA, paramDA;
+ nodeDA.ptr = node->optional;
+ nodeDA.end = nodeDA.ptr + (node->len - QN_LookupNode::NodeSize);
+ paramDA.ptr = param->optional;
+ paramDA.end = paramDA.ptr + (param->len - QN_LookupParameters::NodeSize);
+ err = parseDA(ctx, requestPtr, treeNodePtr,
+ nodeDA, treeBits, paramDA, paramBits);
+ if (unlikely(err != 0))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+
+ if (treeNodePtr.p->m_bits & TreeNode::T_ATTR_INTERPRETED)
+ {
+ jam();
+ LqhKeyReq::setInterpretedFlag(dst->requestInfo, 1);
+ }
+
+ /**
+ * Inherit batch size from parent
+ */
+ treeNodePtr.p->m_batch_size = 1;
+ if (treeNodePtr.p->m_parentPtrI != RNIL)
+ {
+ jam();
+ Ptr<TreeNode> parentPtr;
+ m_treenode_pool.getPtr(parentPtr, treeNodePtr.p->m_parentPtrI);
+ treeNodePtr.p->m_batch_size = parentPtr.p->m_batch_size;
+ }
+
+ if (ctx.m_start_signal)
+ {
+ jam();
+ Signal * signal = ctx.m_start_signal;
+ const LqhKeyReq* src = (const LqhKeyReq*)signal->getDataPtr();
+#if NOT_YET
+ Uint32 instanceNo =
+ blockToInstance(signal->header.theReceiversBlockNumber);
+ treeNodePtr.p->m_send.m_ref = numberToRef(DBLQH,
+ instanceNo, getOwnNodeId());
+#else
+ treeNodePtr.p->m_send.m_ref =
+ numberToRef(DBLQH, getInstanceKey(src->tableSchemaVersion & 0xFFFF,
+ src->fragmentData & 0xFFFF),
+ getOwnNodeId());
+#endif
+
+ Uint32 hashValue = src->hashValue;
+ Uint32 fragId = src->fragmentData;
+ Uint32 requestInfo = src->requestInfo;
+ Uint32 attrLen = src->attrLen; // fragdist-key is in here
+
+ /**
+ * assertions
+ */
+ ndbassert(LqhKeyReq::getAttrLen(attrLen) == 0); // Only long
+ ndbassert(LqhKeyReq::getScanTakeOverFlag(attrLen) == 0);// Not supported
+ ndbassert(LqhKeyReq::getReorgFlag(attrLen) == 0); // Not supported
+ ndbassert(LqhKeyReq::getOperation(requestInfo) == ZREAD);
+ ndbassert(LqhKeyReq::getKeyLen(requestInfo) == 0); // Only long
+ ndbassert(LqhKeyReq::getMarkerFlag(requestInfo) == 0); // Only read
+ ndbassert(LqhKeyReq::getAIInLqhKeyReq(requestInfo) == 0);
+ ndbassert(LqhKeyReq::getSeqNoReplica(requestInfo) == 0);
+ ndbassert(LqhKeyReq::getLastReplicaNo(requestInfo) == 0);
+ ndbassert(LqhKeyReq::getApplicationAddressFlag(requestInfo) != 0);
+ ndbassert(LqhKeyReq::getSameClientAndTcFlag(requestInfo) == 0);
+
+#if TODO
+ /**
+ * Handle various lock-modes
+ */
+ static Uint8 getDirtyFlag(const UintR & requestInfo);
+ static Uint8 getSimpleFlag(const UintR & requestInfo);
+#endif
+
+ Uint32 dst_requestInfo = dst->requestInfo;
+ ndbassert(LqhKeyReq::getInterpretedFlag(requestInfo) ==
+ LqhKeyReq::getInterpretedFlag(dst_requestInfo));
+ ndbassert(LqhKeyReq::getNoDiskFlag(requestInfo) ==
+ LqhKeyReq::getNoDiskFlag(dst_requestInfo));
+
+ dst->hashValue = hashValue;
+ dst->fragmentData = fragId;
+ dst->attrLen = attrLen; // fragdist is in here
+
+ treeNodePtr.p->m_send.m_keyInfoPtrI = ctx.m_keyPtr.i;
+ treeNodePtr.p->m_bits |= TreeNode::T_ONE_SHOT;
+ }
+ return 0;
+ } while (0);
+
+ return err;
+}
+
+void
+Dbspj::lookup_start(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ lookup_send(signal, requestPtr, treeNodePtr);
+}
+
+void
+Dbspj::lookup_send(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ jam();
+
+ Uint32 cnt = 2;
+ if (treeNodePtr.p->isLeaf())
+ {
+ jam();
+ if (requestPtr.p->isLookup())
+ {
+ jam();
+ cnt = 0;
+ }
+ else
+ {
+ jam();
+ cnt = 1;
+ }
+ }
+
+ LqhKeyReq* req = reinterpret_cast<LqhKeyReq*>(signal->getDataPtrSend());
+
+ memcpy(req, treeNodePtr.p->m_lookup_data.m_lqhKeyReq,
+ sizeof(treeNodePtr.p->m_lookup_data.m_lqhKeyReq));
+ req->variableData[2] = treeNodePtr.p->m_send.m_correlation;
+ req->variableData[3] = requestPtr.p->m_rootResultData;
+
+ if (!(requestPtr.p->isLookup() && treeNodePtr.p->isLeaf()))
+ {
+ // Non-LEAF want reply to SPJ instead of ApiClient.
+ LqhKeyReq::setNormalProtocolFlag(req->requestInfo, 1);
+ req->variableData[0] = reference();
+ req->variableData[1] = treeNodePtr.i;
+ }
+ else
+ {
+ jam();
+ /**
+ * Fake that TC sent this request,
+ * so that it can route a maybe TCKEYREF
+ */
+ req->tcBlockref = requestPtr.p->m_senderRef;
+ }
+
+ SectionHandle handle(this);
+
+ Uint32 ref = treeNodePtr.p->m_send.m_ref;
+ Uint32 keyInfoPtrI = treeNodePtr.p->m_send.m_keyInfoPtrI;
+ Uint32 attrInfoPtrI = treeNodePtr.p->m_send.m_attrInfoPtrI;
+
+ if (treeNodePtr.p->m_bits & TreeNode::T_ONE_SHOT)
+ {
+ jam();
+ /**
+ * Pass sections to send
+ */
+ treeNodePtr.p->m_send.m_attrInfoPtrI = RNIL;
+ treeNodePtr.p->m_send.m_keyInfoPtrI = RNIL;
+ }
+ else
+ {
+ if ((treeNodePtr.p->m_bits & TreeNode::T_KEYINFO_CONSTRUCTED) == 0)
+ {
+ jam();
+ Uint32 tmp = RNIL;
+ ndbrequire(dupSection(tmp, keyInfoPtrI)); // TODO handle error
+ keyInfoPtrI = tmp;
+ }
+ else
+ {
+ jam();
+ treeNodePtr.p->m_send.m_keyInfoPtrI = RNIL;
+ }
+
+ if ((treeNodePtr.p->m_bits & TreeNode::T_ATTRINFO_CONSTRUCTED) == 0)
+ {
+ jam();
+ Uint32 tmp = RNIL;
+ ndbrequire(dupSection(tmp, attrInfoPtrI)); // TODO handle error
+ attrInfoPtrI = tmp;
+ }
+ else
+ {
+ jam();
+ treeNodePtr.p->m_send.m_attrInfoPtrI = RNIL;
+ }
+ }
+
+ getSection(handle.m_ptr[0], keyInfoPtrI);
+ getSection(handle.m_ptr[1], attrInfoPtrI);
+ handle.m_cnt = 2;
+
+#if defined DEBUG_LQHKEYREQ
+ ndbout_c("LQHKEYREQ to %x", ref);
+ printLQHKEYREQ(stdout, signal->getDataPtrSend(),
+ NDB_ARRAY_SIZE(treeNodePtr.p->m_lookup_data.m_lqhKeyReq),
+ DBLQH);
+ printf("KEYINFO: ");
+ print(handle.m_ptr[0], stdout);
+ printf("ATTRINFO: ");
+ print(handle.m_ptr[1], stdout);
+#endif
+
+ Uint32 Tnode = refToNode(ref);
+ if (Tnode == getOwnNodeId())
+ {
+ c_Counters.incr_counter(CI_LOCAL_READS_SENT, 1);
+ }
+ else
+ {
+ c_Counters.incr_counter(CI_REMOTE_READS_SENT, 1);
+ }
+
+ if (unlikely(!c_alive_nodes.get(Tnode)))
+ {
+ jam();
+ releaseSections(handle);
+ abort(signal, requestPtr, DbspjErr::NodeFailure);
+ return;
+ }
+ else if (! (treeNodePtr.p->isLeaf() && requestPtr.p->isLookup()))
+ {
+ jam();
+ ndbassert(Tnode < NDB_ARRAY_SIZE(requestPtr.p->m_lookup_node_data));
+ requestPtr.p->m_outstanding += cnt;
+ requestPtr.p->m_lookup_node_data[Tnode] += cnt;
+ // number wrapped
+ ndbrequire(! (requestPtr.p->m_lookup_node_data[Tnode] == 0));
+ }
+
+ sendSignal(ref, GSN_LQHKEYREQ, signal,
+ NDB_ARRAY_SIZE(treeNodePtr.p->m_lookup_data.m_lqhKeyReq),
+ JBB, &handle);
+
+ treeNodePtr.p->m_lookup_data.m_outstanding += cnt;
+ if (requestPtr.p->isLookup() && treeNodePtr.p->isLeaf())
+ {
+ jam();
+ /**
+ * Send TCKEYCONF with DirtyReadBit + Tnode,
+ * so that API can discover if Tnode while waiting for result
+ */
+ Uint32 resultRef = req->variableData[0];
+ Uint32 resultData = req->variableData[1];
+
+ TcKeyConf* conf = (TcKeyConf*)signal->getDataPtrSend();
+ conf->apiConnectPtr = RNIL; // lookup transaction from operations...
+ conf->confInfo = 0;
+ TcKeyConf::setNoOfOperations(conf->confInfo, 1);
+ conf->transId1 = requestPtr.p->m_transId[0];
+ conf->transId2 = requestPtr.p->m_transId[1];
+ conf->operations[0].apiOperationPtr = resultData;
+ conf->operations[0].attrInfoLen = TcKeyConf::DirtyReadBit | Tnode;
+ Uint32 sigLen = TcKeyConf::StaticLength + TcKeyConf::OperationLength;
+ sendTCKEYCONF(signal, sigLen, resultRef, requestPtr.p->m_senderRef);
+ }
+}
+
+void
+Dbspj::lookup_execTRANSID_AI(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr,
+ const RowPtr & rowRef)
+{
+ jam();
+
+ Uint32 Tnode = refToNode(signal->getSendersBlockRef());
+
+ {
+ LocalArenaPoolImpl pool(requestPtr.p->m_arena, m_dependency_map_pool);
+ Local_dependency_map list(pool, treeNodePtr.p->m_dependent_nodes);
+ Dependency_map::ConstDataBufferIterator it;
+ for (list.first(it); !it.isNull(); list.next(it))
+ {
+ jam();
+ Ptr<TreeNode> childPtr;
+ m_treenode_pool.getPtr(childPtr, * it.data);
+ ndbrequire(childPtr.p->m_info != 0&&childPtr.p->m_info->m_parent_row!=0);
+ (this->*(childPtr.p->m_info->m_parent_row))(signal,
+ requestPtr, childPtr,rowRef);
+ }
+ }
+ ndbrequire(!(requestPtr.p->isLookup() && treeNodePtr.p->isLeaf()));
+
+ ndbassert(requestPtr.p->m_lookup_node_data[Tnode] >= 1);
+ requestPtr.p->m_lookup_node_data[Tnode] -= 1;
+
+ treeNodePtr.p->m_lookup_data.m_outstanding--;
+
+ if (treeNodePtr.p->m_bits & TreeNode::T_REPORT_BATCH_COMPLETE
+ && treeNodePtr.p->m_lookup_data.m_parent_batch_complete
+ && treeNodePtr.p->m_lookup_data.m_outstanding == 0)
+ {
+ jam();
+ // We have received all rows for this operation in this batch.
+ reportBatchComplete(signal, requestPtr, treeNodePtr);
+
+ // Prepare for next batch.
+ treeNodePtr.p->m_lookup_data.m_parent_batch_complete = false;
+ treeNodePtr.p->m_lookup_data.m_outstanding = 0;
+ }
+
+ checkBatchComplete(signal, requestPtr, 1);
+}
+
+void
+Dbspj::lookup_execLQHKEYREF(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ const LqhKeyRef * rep = (LqhKeyRef*)signal->getDataPtr();
+ Uint32 errCode = rep->errorCode;
+ Uint32 Tnode = refToNode(signal->getSendersBlockRef());
+
+ c_Counters.incr_counter(CI_READS_NOT_FOUND, 1);
+
+ if (requestPtr.p->isLookup())
+ {
+ jam();
+
+ /* CONF/REF not requested for lookup-Leaf: */
+ ndbrequire(!treeNodePtr.p->isLeaf());
+
+ /**
+ * Scan-request does not need to
+ * send TCKEYREF...
+ */
+ /**
+ * Return back to api...
+ * NOTE: assume that signal is tampered with
+ */
+ Uint32 resultRef = treeNodePtr.p->m_lookup_data.m_api_resultRef;
+ Uint32 resultData = treeNodePtr.p->m_lookup_data.m_api_resultData;
+ TcKeyRef* ref = (TcKeyRef*)signal->getDataPtr();
+ ref->connectPtr = resultData;
+ ref->transId[0] = requestPtr.p->m_transId[0];
+ ref->transId[1] = requestPtr.p->m_transId[1];
+ ref->errorCode = errCode;
+ ref->errorData = 0;
+
+ DEBUG("lookup_execLQHKEYREF, errorCode:" << errCode);
+
+ sendTCKEYREF(signal, resultRef, requestPtr.p->m_senderRef);
+
+ if (treeNodePtr.p->m_bits & TreeNode::T_UNIQUE_INDEX_LOOKUP)
+ {
+ /**
+ * If this is a "leaf" unique index lookup
+ * emit extra TCKEYCONF as would have been done with ordinary
+ * operation
+ */
+ LocalArenaPoolImpl pool(requestPtr.p->m_arena, m_dependency_map_pool);
+ Local_dependency_map list(pool, treeNodePtr.p->m_dependent_nodes);
+ Dependency_map::ConstDataBufferIterator it;
+ ndbrequire(list.first(it));
+ ndbrequire(list.getSize() == 1); // should only be 1 child
+ Ptr<TreeNode> childPtr;
+ m_treenode_pool.getPtr(childPtr, * it.data);
+ if (childPtr.p->m_bits & TreeNode::T_LEAF)
+ {
+ jam();
+ Uint32 resultRef = childPtr.p->m_lookup_data.m_api_resultRef;
+ Uint32 resultData = childPtr.p->m_lookup_data.m_api_resultData;
+ TcKeyConf* conf = (TcKeyConf*)signal->getDataPtr();
+ conf->apiConnectPtr = RNIL;
+ conf->confInfo = 0;
+ conf->gci_hi = 0;
+ TcKeyConf::setNoOfOperations(conf->confInfo, 1);
+ conf->transId1 = requestPtr.p->m_transId[0];
+ conf->transId2 = requestPtr.p->m_transId[1];
+ conf->operations[0].apiOperationPtr = resultData;
+ conf->operations[0].attrInfoLen =
+ TcKeyConf::DirtyReadBit |getOwnNodeId();
+ sendTCKEYCONF(signal, TcKeyConf::StaticLength + 2, resultRef, requestPtr.p->m_senderRef);
+ }
+ }
+ }
+ else
+ {
+ jam();
+ switch(errCode){
+ case 626: // Row not found
+ case 899: // Interpreter_exit_nok
+ jam();
+ break;
+ default:
+ jam();
+ abort(signal, requestPtr, errCode);
+ }
+ }
+
+ Uint32 cnt = 2;
+ if (treeNodePtr.p->isLeaf()) // Can't be a lookup-Leaf, asserted above
+ cnt = 1;
+
+ ndbassert(requestPtr.p->m_lookup_node_data[Tnode] >= cnt);
+ requestPtr.p->m_lookup_node_data[Tnode] -= cnt;
+
+ treeNodePtr.p->m_lookup_data.m_outstanding -= cnt;
+
+ if (treeNodePtr.p->m_bits & TreeNode::T_REPORT_BATCH_COMPLETE
+ && treeNodePtr.p->m_lookup_data.m_parent_batch_complete
+ && treeNodePtr.p->m_lookup_data.m_outstanding == 0)
+ {
+ jam();
+ // We have received all rows for this operation in this batch.
+ reportBatchComplete(signal, requestPtr, treeNodePtr);
+
+ // Prepare for next batch.
+ treeNodePtr.p->m_lookup_data.m_parent_batch_complete = false;
+ treeNodePtr.p->m_lookup_data.m_outstanding = 0;
+ }
+
+ checkBatchComplete(signal, requestPtr, cnt);
+}
+
+void
+Dbspj::lookup_execLQHKEYCONF(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ ndbrequire(!(requestPtr.p->isLookup() && treeNodePtr.p->isLeaf()));
+
+ Uint32 Tnode = refToNode(signal->getSendersBlockRef());
+
+ if (treeNodePtr.p->m_bits & TreeNode::T_USER_PROJECTION)
+ {
+ jam();
+ requestPtr.p->m_rows++;
+ }
+
+ ndbassert(requestPtr.p->m_lookup_node_data[Tnode] >= 1);
+ requestPtr.p->m_lookup_node_data[Tnode] -= 1;
+
+ treeNodePtr.p->m_lookup_data.m_outstanding--;
+
+ if (treeNodePtr.p->m_bits & TreeNode::T_REPORT_BATCH_COMPLETE
+ && treeNodePtr.p->m_lookup_data.m_parent_batch_complete
+ && treeNodePtr.p->m_lookup_data.m_outstanding == 0)
+ {
+ jam();
+ // We have received all rows for this operation in this batch.
+ reportBatchComplete(signal, requestPtr, treeNodePtr);
+
+ // Prepare for next batch.
+ treeNodePtr.p->m_lookup_data.m_parent_batch_complete = false;
+ treeNodePtr.p->m_lookup_data.m_outstanding = 0;
+ }
+
+ checkBatchComplete(signal, requestPtr, 1);
+}
+
+void
+Dbspj::lookup_parent_row(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr,
+ const RowPtr & rowRef)
+{
+ /**
+ * Here we need to...
+ * 1) construct a key
+ * 2) compute hash (normally TC)
+ * 3) get node for row (normally TC)
+ */
+ Uint32 err;
+ const LqhKeyReq* src = (LqhKeyReq*)treeNodePtr.p->m_lookup_data.m_lqhKeyReq;
+ const Uint32 tableId = LqhKeyReq::getTableId(src->tableSchemaVersion);
+ const Uint32 corrVal = rowRef.m_src_correlation;
+
+ DEBUG("::lookup_parent_row");
+
+ do
+ {
+ Uint32 ptrI = RNIL;
+ if (treeNodePtr.p->m_bits & TreeNode::T_KEYINFO_CONSTRUCTED)
+ {
+ jam();
+ DEBUG("parent_row w/ T_KEYINFO_CONSTRUCTED");
+ /**
+ * Get key-pattern
+ */
+ LocalArenaPoolImpl pool(requestPtr.p->m_arena, m_dependency_map_pool);
+ Local_pattern_store pattern(pool, treeNodePtr.p->m_keyPattern);
+
+ bool keyIsNull;
+ err = expand(ptrI, pattern, rowRef, keyIsNull);
+ if (unlikely(err != 0))
+ break;
+
+ if (keyIsNull)
+ {
+ jam();
+ DEBUG("Key contain NULL values");
+ /**
+ * When the key contains NULL values, an EQ-match is impossible!
+ * Entire lookup request can therefore be eliminate as it is known
+ * to be REFused with errorCode = 626 (Row not found).
+ * Different handling is required depening of request being a
+ * scan or lookup:
+ */
+ if (requestPtr.p->isScan())
+ {
+ /**
+ * Scan request: We can simply ignore lookup operation:
+ * As rowCount in SCANCONF will not include this KEYREQ,
+ * we dont have to send a KEYREF either.
+ */
+ jam();
+ DEBUG("..Ignore impossible KEYREQ");
+ if (ptrI != RNIL)
+ {
+ releaseSection(ptrI);
+ }
+ return; // Bailout, KEYREQ would have returned KEYREF(626) anyway
+ }
+ else // isLookup()
+ {
+ /**
+ * Ignored lookup request need a faked KEYREF for the lookup operation.
+ * Furthermore, if this is a leaf treeNode, a KEYCONF is also
+ * expected by the API.
+ *
+ * TODO: Not implemented yet as we believe
+ * elimination of NULL key access for scan request
+ * will have the most performance impact.
+ */
+ jam();
+ }
+ } // keyIsNull
+
+ /**
+ * NOTE:
+ * The logic below contradicts 'keyIsNull' logic above and should
+ * be removed.
+ * However, it's likely that scanIndex should have similar
+ * logic as 'Null as wildcard' may make sense for a range bound.
+ * NOTE2:
+ * Until 'keyIsNull' also cause bailout for request->isLookup()
+ * createEmptySection *is* require to avoid crash due to empty keys.
+ */
+ if (ptrI == RNIL) // TODO: remove when keyIsNull is completely handled
+ {
+ jam();
+ /**
+ * We constructed a null-key...construct a zero-length key (even if we don't support it *now*)
+ *
+ * (we actually did prior to joining mysql where null was treated as any other
+ * value in a key). But mysql treats null in unique key as *wildcard*
+ * which we don't support so well...and do nasty tricks in handler
+ *
+ * NOTE: should be *after* check for error
+ */
+ err = createEmptySection(ptrI);
+ if (unlikely(err != 0))
+ break;
+ }
+
+ treeNodePtr.p->m_send.m_keyInfoPtrI = ptrI;
+ }
+
+ BuildKeyReq tmp;
+ err = computeHash(signal, tmp, tableId, treeNodePtr.p->m_send.m_keyInfoPtrI);
+ if (unlikely(err != 0))
+ break;
+
+ err = getNodes(signal, tmp, tableId);
+ if (unlikely(err != 0))
+ break;
+
+ Uint32 attrInfoPtrI = treeNodePtr.p->m_send.m_attrInfoPtrI;
+ if (treeNodePtr.p->m_bits & TreeNode::T_ATTRINFO_CONSTRUCTED)
+ {
+ jam();
+ Uint32 tmp = RNIL;
+ ndbrequire(dupSection(tmp, attrInfoPtrI)); // TODO handle error
+
+ Uint32 org_size;
+ {
+ SegmentedSectionPtr ptr;
+ getSection(ptr, tmp);
+ org_size = ptr.sz;
+ }
+
+ bool hasNull;
+ LocalArenaPoolImpl pool(requestPtr.p->m_arena, m_dependency_map_pool);
+ Local_pattern_store pattern(pool, treeNodePtr.p->m_attrParamPattern);
+ err = expand(tmp, pattern, rowRef, hasNull);
+ if (unlikely(err != 0))
+ break;
+// ndbrequire(!hasNull);
+
+ /**
+ * Update size of subsrouting section, which contains arguments
+ */
+ SegmentedSectionPtr ptr;
+ getSection(ptr, tmp);
+ Uint32 new_size = ptr.sz;
+ Uint32 * sectionptrs = ptr.p->theData;
+ sectionptrs[4] = new_size - org_size;
+
+ treeNodePtr.p->m_send.m_attrInfoPtrI = tmp;
+ }
+
+ /**
+ * Now send...
+ */
+
+ /**
+ * TODO merge better with lookup_start (refactor)
+ */
+ {
+ /* We set the upper half word of m_correlation to the tuple ID
+ * of the parent, such that the API can match this tuple with its
+ * parent.
+ * Then we re-use the tuple ID of the parent as the
+ * tuple ID for this tuple also. Since the tuple ID
+ * is unique within this batch and SPJ block for the parent operation,
+ * it must also be unique for this operation.
+ * This ensures that lookup operations with no user projection will
+ * work, since such operations will have the same tuple ID as their
+ * parents. The API will then be able to match a tuple with its
+ * grandparent, even if it gets no tuple for the parent operation.*/
+ treeNodePtr.p->m_send.m_correlation =
+ (corrVal << 16) + (corrVal & 0xffff);
+
+ treeNodePtr.p->m_send.m_ref = tmp.receiverRef;
+ LqhKeyReq * dst = (LqhKeyReq*)treeNodePtr.p->m_lookup_data.m_lqhKeyReq;
+ dst->hashValue = tmp.hashInfo[0];
+ dst->fragmentData = tmp.fragId;
+ Uint32 attrLen = 0;
+ LqhKeyReq::setDistributionKey(attrLen, tmp.fragDistKey);
+ dst->attrLen = attrLen;
+ lookup_send(signal, requestPtr, treeNodePtr);
+
+ if (treeNodePtr.p->m_bits & TreeNode::T_ATTRINFO_CONSTRUCTED)
+ {
+ jam();
+ // restore
+ treeNodePtr.p->m_send.m_attrInfoPtrI = attrInfoPtrI;
+ }
+ }
+ return;
+ } while (0);
+
+ ndbrequire(false);
+}
+
+void
+Dbspj::lookup_parent_batch_complete(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ jam();
+
+ /**
+ * lookups are performed directly...so we're not really interested in
+ * parent_batch_complete...we only pass-through
+ */
+
+ /**
+ * but this method should only be called if we have T_REPORT_BATCH_COMPLETE
+ */
+ ndbassert(treeNodePtr.p->m_bits & TreeNode::T_REPORT_BATCH_COMPLETE);
+
+ ndbassert(!treeNodePtr.p->m_lookup_data.m_parent_batch_complete);
+ treeNodePtr.p->m_lookup_data.m_parent_batch_complete = true;
+ if (treeNodePtr.p->m_bits & TreeNode::T_REPORT_BATCH_COMPLETE
+ && treeNodePtr.p->m_lookup_data.m_outstanding == 0)
+ {
+ jam();
+ // We have received all rows for this operation in this batch.
+ reportBatchComplete(signal, requestPtr, treeNodePtr);
+
+ // Prepare for next batch.
+ treeNodePtr.p->m_lookup_data.m_parent_batch_complete = false;
+ treeNodePtr.p->m_lookup_data.m_outstanding = 0;
+ }
+}
+
+void
+Dbspj::lookup_abort(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ jam();
+}
+
+Uint32
+Dbspj::lookup_execNODE_FAILREP(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr,
+ NdbNodeBitmask mask)
+{
+ jam();
+ Uint32 node = 0;
+ Uint32 sum = 0;
+ while (requestPtr.p->m_outstanding &&
+ ((node = mask.find(node + 1)) != NdbNodeBitmask::NotFound))
+ {
+ Uint32 cnt = requestPtr.p->m_lookup_node_data[node];
+ sum += cnt;
+ requestPtr.p->m_lookup_node_data[node] = 0;
+ }
+
+ if (sum)
+ {
+ jam();
+ ndbrequire(requestPtr.p->m_outstanding >= sum);
+ requestPtr.p->m_outstanding -= sum;
+ }
+
+ return sum;
+}
+
+void
+Dbspj::lookup_cleanup(Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ cleanup_common(requestPtr, treeNodePtr);
+}
+
+
+Uint32
+Dbspj::handle_special_hash(Uint32 tableId, Uint32 dstHash[4],
+ const Uint64* src,
+ Uint32 srcLen, // Len in #32bit words
+ const KeyDescriptor* desc)
+{
+ const Uint32 MAX_KEY_SIZE_IN_LONG_WORDS=
+ (MAX_KEY_SIZE_IN_WORDS + 1) / 2;
+ Uint64 alignedWorkspace[MAX_KEY_SIZE_IN_LONG_WORDS * MAX_XFRM_MULTIPLY];
+ const bool hasVarKeys = desc->noOfVarKeys > 0;
+ const bool hasCharAttr = desc->hasCharAttr;
+ const bool compute_distkey = desc->noOfDistrKeys > 0;
+
+ const Uint64 *hashInput = 0;
+ Uint32 inputLen = 0;
+ Uint32 keyPartLen[MAX_ATTRIBUTES_IN_INDEX];
+ Uint32 * keyPartLenPtr;
+
+ /* Normalise KeyInfo into workspace if necessary */
+ if (hasCharAttr || (compute_distkey && hasVarKeys))
+ {
+ hashInput = alignedWorkspace;
+ keyPartLenPtr = keyPartLen;
+ inputLen = xfrm_key(tableId,
+ (Uint32*)src,
+ (Uint32*)alignedWorkspace,
+ sizeof(alignedWorkspace) >> 2,
+ keyPartLenPtr);
+ if (unlikely(inputLen == 0))
+ {
+ return 290; // 'Corrupt key in TC, unable to xfrm'
+ }
+ }
+ else
+ {
+ /* Keyinfo already suitable for hash */
+ hashInput = src;
+ inputLen = srcLen;
+ keyPartLenPtr = 0;
+ }
+
+ /* Calculate primary key hash */
+ md5_hash(dstHash, hashInput, inputLen);
+
+ /* If the distribution key != primary key then we have to
+ * form a distribution key from the primary key and calculate
+ * a separate distribution hash based on this
+ */
+ if (compute_distkey)
+ {
+ jam();
+
+ Uint32 distrKeyHash[4];
+ /* Reshuffle primary key columns to get just distribution key */
+ Uint32 len = create_distr_key(tableId, (Uint32*)hashInput, (Uint32*)alignedWorkspace, keyPartLenPtr);
+ /* Calculate distribution key hash */
+ md5_hash(distrKeyHash, alignedWorkspace, len);
+
+ /* Just one word used for distribution */
+ dstHash[1] = distrKeyHash[1];
+ }
+ return 0;
+}
+
+Uint32
+Dbspj::computeHash(Signal* signal,
+ BuildKeyReq& dst, Uint32 tableId, Uint32 ptrI)
+{
+ /**
+ * Essentially the same code as in Dbtc::hash().
+ * The code for user defined partitioning has been removed though.
+ */
+ SegmentedSectionPtr ptr;
+ getSection(ptr, ptrI);
+
+ /* NOTE: md5_hash below require 64-bit alignment
+ */
+ const Uint32 MAX_KEY_SIZE_IN_LONG_WORDS=
+ (MAX_KEY_SIZE_IN_WORDS + 1) / 2;
+ Uint64 tmp64[MAX_KEY_SIZE_IN_LONG_WORDS];
+ Uint32 *tmp32 = (Uint32*)tmp64;
+ copy(tmp32, ptr);
+
+ const KeyDescriptor* desc = g_key_descriptor_pool.getPtr(tableId);
+ ndbrequire(desc != NULL);
+
+ bool need_special_hash = desc->hasCharAttr | (desc->noOfDistrKeys > 0);
+ if (need_special_hash)
+ {
+ jam();
+ return handle_special_hash(tableId, dst.hashInfo, tmp64, ptr.sz, desc);
+ }
+ else
+ {
+ jam();
+ md5_hash(dst.hashInfo, tmp64, ptr.sz);
+ return 0;
+ }
+}
+
+/**
+ * This function differs from computeHash in that *ptrI*
+ * only contains partition key (packed) and not full primary key
+ */
+Uint32
+Dbspj::computePartitionHash(Signal* signal,
+ BuildKeyReq& dst, Uint32 tableId, Uint32 ptrI)
+{
+ SegmentedSectionPtr ptr;
+ getSection(ptr, ptrI);
+
+ /* NOTE: md5_hash below require 64-bit alignment
+ */
+ const Uint32 MAX_KEY_SIZE_IN_LONG_WORDS=
+ (MAX_KEY_SIZE_IN_WORDS + 1) / 2;
+ Uint64 _space[MAX_KEY_SIZE_IN_LONG_WORDS];
+ Uint64 *tmp64 = _space;
+ Uint32 *tmp32 = (Uint32*)tmp64;
+ Uint32 sz = ptr.sz;
+ copy(tmp32, ptr);
+
+ const KeyDescriptor* desc = g_key_descriptor_pool.getPtr(tableId);
+ ndbrequire(desc != NULL);
+
+ bool need_xfrm = desc->hasCharAttr || desc->noOfVarKeys;
+ if (need_xfrm)
+ {
+ jam();
+ /**
+ * xfrm distribution key
+ */
+ Uint32 srcPos = 0;
+ Uint32 dstPos = 0;
+ Uint32 * src = tmp32;
+ Uint32 * dst = signal->theData+24;
+ for (Uint32 i = 0; i < desc->noOfKeyAttr; i++)
+ {
+ const KeyDescriptor::KeyAttr& keyAttr = desc->keyAttr[i];
+ if (AttributeDescriptor::getDKey(keyAttr.attributeDescriptor))
+ {
+ xfrm_attr(keyAttr.attributeDescriptor, keyAttr.charsetInfo,
+ src, srcPos, dst, dstPos,
+ NDB_ARRAY_SIZE(signal->theData) - 24);
+ }
+ }
+ tmp64 = (Uint64*)dst;
+ sz = dstPos;
+ }
+
+ md5_hash(dst.hashInfo, tmp64, sz);
+ return 0;
+}
+
+Uint32
+Dbspj::getNodes(Signal* signal, BuildKeyReq& dst, Uint32 tableId)
+{
+ Uint32 err;
+ DiGetNodesReq * req = (DiGetNodesReq *)&signal->theData[0];
+ req->tableId = tableId;
+ req->hashValue = dst.hashInfo[1];
+ req->distr_key_indicator = 0; // userDefinedPartitioning not supported!
+
+#if 1
+ EXECUTE_DIRECT(DBDIH, GSN_DIGETNODESREQ, signal,
+ DiGetNodesReq::SignalLength);
+#else
+ sendSignal(DBDIH_REF, GSN_DIGETNODESREQ, signal,
+ DiGetNodesReq::SignalLength, JBB);
+ jamEntry();
+
+#endif
+
+ DiGetNodesConf * conf = (DiGetNodesConf *)&signal->theData[0];
+ err = signal->theData[0];
+ Uint32 Tdata2 = conf->reqinfo;
+ Uint32 nodeId = conf->nodes[0];
+ Uint32 instanceKey = (Tdata2 >> 24) & 127;
+
+ DEBUG("HASH to nodeId:" << nodeId << ", instanceKey:" << instanceKey);
+
+ jamEntry();
+ if (unlikely(err != 0))
+ goto error;
+
+ dst.fragId = conf->fragId;
+ dst.fragDistKey = (Tdata2 >> 16) & 255;
+ dst.receiverRef = numberToRef(DBLQH, instanceKey, nodeId);
+
+ return 0;
+
+error:
+ /**
+ * TODO handle error
+ */
+ ndbrequire(false);
+ return err;
+}
+
+/**
+ * END - MODULE LOOKUP
+ */
+
+/**
+ * MODULE SCAN FRAG
+ *
+ * NOTE: This may only be root node
+ */
+const Dbspj::OpInfo
+Dbspj::g_ScanFragOpInfo =
+{
+ &Dbspj::scanFrag_build,
+ 0, // prepare
+ &Dbspj::scanFrag_start,
+ &Dbspj::scanFrag_execTRANSID_AI,
+ 0, // execLQHKEYREF
+ 0, // execLQHKEYCONF
+ &Dbspj::scanFrag_execSCAN_FRAGREF,
+ &Dbspj::scanFrag_execSCAN_FRAGCONF,
+ 0, // parent row
+ 0, // parent batch complete
+ &Dbspj::scanFrag_execSCAN_NEXTREQ,
+ 0, // Dbspj::scanFrag_complete
+ &Dbspj::scanFrag_abort,
+ 0, // execNODE_FAILREP,
+ &Dbspj::scanFrag_cleanup
+};
+
+Uint32
+Dbspj::scanFrag_build(Build_context& ctx,
+ Ptr<Request> requestPtr,
+ const QueryNode* qn,
+ const QueryNodeParameters* qp)
+{
+ Uint32 err = 0;
+ Ptr<TreeNode> treeNodePtr;
+ const QN_ScanFragNode * node = (const QN_ScanFragNode*)qn;
+ const QN_ScanFragParameters * param = (const QN_ScanFragParameters*)qp;
+
+ do
+ {
+ err = createNode(ctx, requestPtr, treeNodePtr);
+ if (unlikely(err != 0))
+ break;
+
+ treeNodePtr.p->m_scanfrag_data.m_scanFragHandlePtrI = RNIL;
+ Ptr<ScanFragHandle> scanFragHandlePtr;
+ if (unlikely(m_scanfraghandle_pool.seize(requestPtr.p->m_arena,
+ scanFragHandlePtr) != true))
+ {
+ err = DbspjErr::OutOfQueryMemory;
+ break;
+ }
+
+ scanFragHandlePtr.p->m_treeNodePtrI = treeNodePtr.i;
+ scanFragHandlePtr.p->m_state = ScanFragHandle::SFH_NOT_STARTED;
+ treeNodePtr.p->m_scanfrag_data.m_scanFragHandlePtrI = scanFragHandlePtr.i;
+
+ requestPtr.p->m_bits |= Request::RT_SCAN;
+ treeNodePtr.p->m_info = &g_ScanFragOpInfo;
+ treeNodePtr.p->m_bits |= TreeNode::T_ATTR_INTERPRETED;
+ treeNodePtr.p->m_batch_size = ctx.m_batch_size_rows;
+
+ ScanFragReq*dst=(ScanFragReq*)treeNodePtr.p->m_scanfrag_data.m_scanFragReq;
+ dst->senderData = scanFragHandlePtr.i;
+ dst->resultRef = reference();
+ dst->resultData = treeNodePtr.i;
+ dst->savePointId = ctx.m_savepointId;
+
+ Uint32 transId1 = requestPtr.p->m_transId[0];
+ Uint32 transId2 = requestPtr.p->m_transId[1];
+ dst->transId1 = transId1;
+ dst->transId2 = transId2;
+
+ Uint32 treeBits = node->requestInfo;
+ Uint32 paramBits = param->requestInfo;
+ //ndbout_c("Dbspj::scanFrag_build() treeBits=%.8x paramBits=%.8x",
+ // treeBits, paramBits);
+ Uint32 requestInfo = 0;
+ ScanFragReq::setReadCommittedFlag(requestInfo, 1);
+ ScanFragReq::setScanPrio(requestInfo, ctx.m_scanPrio);
+ ScanFragReq::setCorrFactorFlag(requestInfo, 1);
+ ScanFragReq::setNoDiskFlag(requestInfo,
+ (treeBits & DABits::NI_LINKED_DISK) == 0 &&
+ (paramBits & DABits::PI_DISK_ATTR) == 0);
+ dst->requestInfo = requestInfo;
+
+ err = DbspjErr::InvalidTreeNodeSpecification;
+ DEBUG("scanFrag_build: len=" << node->len);
+ if (unlikely(node->len < QN_ScanFragNode::NodeSize))
+ break;
+
+ dst->tableId = node->tableId;
+ dst->schemaVersion = node->tableVersion;
+
+ err = DbspjErr::InvalidTreeParametersSpecification;
+ DEBUG("param len: " << param->len);
+ if (unlikely(param->len < QN_ScanFragParameters::NodeSize))
+ {
+ jam();
+ DEBUG_CRASH();
+ break;
+ }
+
+ ctx.m_resultData = param->resultData;
+
+ /**
+ * Parse stuff common lookup/scan-frag
+ */
+ struct DABuffer nodeDA, paramDA;
+ nodeDA.ptr = node->optional;
+ nodeDA.end = nodeDA.ptr + (node->len - QN_ScanFragNode::NodeSize);
+ paramDA.ptr = param->optional;
+ paramDA.end = paramDA.ptr + (param->len - QN_ScanFragParameters::NodeSize);
+ err = parseDA(ctx, requestPtr, treeNodePtr,
+ nodeDA, treeBits, paramDA, paramBits);
+ if (unlikely(err != 0))
+ {
+ jam();
+ DEBUG_CRASH();
+ break;
+ }
+
+ ctx.m_scan_cnt++;
+ /**
+ * In the scenario with only 1 scan in tree,
+ * register cursor here, so we don't need to search for in after build
+ * If m_scan_cnt > 1,
+ * then this list will simply be cleared after build
+ */
+ registerCursor(requestPtr, treeNodePtr);
+
+ if (ctx.m_start_signal)
+ {
+ jam();
+ Signal* signal = ctx.m_start_signal;
+ const ScanFragReq* src = (const ScanFragReq*)(signal->getDataPtr());
+
+#if NOT_YET
+ Uint32 instanceNo =
+ blockToInstance(signal->header.theReceiversBlockNumber);
+ treeNodePtr.p->m_send.m_ref = numberToRef(DBLQH,
+ instanceNo, getOwnNodeId());
+#else
+ treeNodePtr.p->m_send.m_ref =
+ numberToRef(DBLQH, getInstanceKey(src->tableId,
+ src->fragmentNoKeyLen),
+ getOwnNodeId());
+#endif
+
+ Uint32 fragId = src->fragmentNoKeyLen;
+ Uint32 requestInfo = src->requestInfo;
+ Uint32 batch_size_bytes = src->batch_size_bytes;
+ Uint32 batch_size_rows = src->batch_size_rows;
+
+#ifdef VM_TRACE
+ Uint32 savePointId = src->savePointId;
+ Uint32 tableId = src->tableId;
+ Uint32 schemaVersion = src->schemaVersion;
+ Uint32 transId1 = src->transId1;
+ Uint32 transId2 = src->transId2;
+#endif
+ ndbassert(ScanFragReq::getLockMode(requestInfo) == 0);
+ ndbassert(ScanFragReq::getHoldLockFlag(requestInfo) == 0);
+ ndbassert(ScanFragReq::getKeyinfoFlag(requestInfo) == 0);
+ ndbassert(ScanFragReq::getReadCommittedFlag(requestInfo) == 1);
+ ndbassert(ScanFragReq::getLcpScanFlag(requestInfo) == 0);
+ //ScanFragReq::getAttrLen(requestInfo); // ignore
+ ndbassert(ScanFragReq::getReorgFlag(requestInfo) == 0);
+
+ Uint32 tupScanFlag = ScanFragReq::getTupScanFlag(requestInfo);
+ Uint32 rangeScanFlag = ScanFragReq::getRangeScanFlag(requestInfo);
+ Uint32 descendingFlag = ScanFragReq::getDescendingFlag(requestInfo);
+ Uint32 scanPrio = ScanFragReq::getScanPrio(requestInfo);
+
+ Uint32 dst_requestInfo = dst->requestInfo;
+
+ ScanFragReq::setTupScanFlag(dst_requestInfo,tupScanFlag);
+ ScanFragReq::setRangeScanFlag(dst_requestInfo,rangeScanFlag);
+ ScanFragReq::setDescendingFlag(dst_requestInfo,descendingFlag);
+ ScanFragReq::setScanPrio(dst_requestInfo,scanPrio);
+
+ /**
+ * 'NoDiskFlag' should agree with information in treeNode
+ */
+ ndbassert(ScanFragReq::getNoDiskFlag(requestInfo) ==
+ ScanFragReq::getNoDiskFlag(dst_requestInfo));
+
+ dst->fragmentNoKeyLen = fragId;
+ dst->requestInfo = dst_requestInfo;
+ dst->batch_size_bytes = batch_size_bytes;
+ dst->batch_size_rows = batch_size_rows;
+
+#ifdef VM_TRACE
+ ndbassert(dst->savePointId == savePointId);
+ ndbassert(dst->tableId == tableId);
+ ndbassert(dst->schemaVersion == schemaVersion);
+ ndbassert(dst->transId1 == transId1);
+ ndbassert(dst->transId2 == transId2);
+#endif
+
+ treeNodePtr.p->m_send.m_keyInfoPtrI = ctx.m_keyPtr.i;
+ treeNodePtr.p->m_bits |= TreeNode::T_ONE_SHOT;
+
+ if (rangeScanFlag)
+ {
+ c_Counters.incr_counter(CI_RANGE_SCANS_RECEIVED, 1);
+ }
+ else
+ {
+ c_Counters.incr_counter(CI_TABLE_SCANS_RECEIVED, 1);
+ }
+ }
+ else
+ {
+ ndbrequire(false);
+ }
+
+ return 0;
+ } while (0);
+
+ return err;
+}
+
+void
+Dbspj::scanFrag_start(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ scanFrag_send(signal, requestPtr, treeNodePtr);
+}
+
+void
+Dbspj::scanFrag_send(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ jam();
+
+ requestPtr.p->m_outstanding++;
+ requestPtr.p->m_cnt_active++;
+ mark_active(requestPtr, treeNodePtr, true);
+ treeNodePtr.p->m_state = TreeNode::TN_ACTIVE;
+ Ptr<ScanFragHandle> scanFragHandlePtr;
+ m_scanfraghandle_pool.getPtr(scanFragHandlePtr, treeNodePtr.p->
+ m_scanfrag_data.m_scanFragHandlePtrI);
+
+ ScanFragReq* req = reinterpret_cast<ScanFragReq*>(signal->getDataPtrSend());
+
+ memcpy(req, treeNodePtr.p->m_scanfrag_data.m_scanFragReq,
+ sizeof(treeNodePtr.p->m_scanfrag_data.m_scanFragReq));
+ req->variableData[0] = treeNodePtr.p->m_send.m_correlation;
+ req->variableData[1] = requestPtr.p->m_rootResultData;
+
+ SectionHandle handle(this);
+
+ Uint32 ref = treeNodePtr.p->m_send.m_ref;
+ Uint32 keyInfoPtrI = treeNodePtr.p->m_send.m_keyInfoPtrI;
+ Uint32 attrInfoPtrI = treeNodePtr.p->m_send.m_attrInfoPtrI;
+
+ /**
+ * ScanFrag may only be used as root-node, i.e T_ONE_SHOT
+ */
+ ndbrequire(treeNodePtr.p->m_bits & TreeNode::T_ONE_SHOT);
+
+ /**
+ * Pass sections to send
+ */
+ treeNodePtr.p->m_send.m_attrInfoPtrI = RNIL;
+ treeNodePtr.p->m_send.m_keyInfoPtrI = RNIL;
+
+ getSection(handle.m_ptr[0], attrInfoPtrI);
+ handle.m_cnt = 1;
+
+ if (keyInfoPtrI != RNIL)
+ {
+ jam();
+ getSection(handle.m_ptr[1], keyInfoPtrI);
+ handle.m_cnt = 2;
+ }
+
+#ifdef DEBUG_SCAN_FRAGREQ
+ ndbout_c("SCAN_FRAGREQ to %x", ref);
+ printSCAN_FRAGREQ(stdout, signal->getDataPtrSend(),
+ NDB_ARRAY_SIZE(treeNodePtr.p->m_scanfrag_data.m_scanFragReq),
+ DBLQH);
+ printf("ATTRINFO: ");
+ print(handle.m_ptr[0], stdout);
+ if (handle.m_cnt > 1)
+ {
+ printf("KEYINFO: ");
+ print(handle.m_ptr[1], stdout);
+ }
+#endif
+
+ if (ScanFragReq::getRangeScanFlag(req->requestInfo))
+ {
+ c_Counters.incr_counter(CI_LOCAL_RANGE_SCANS_SENT, 1);
+ }
+ else
+ {
+ c_Counters.incr_counter(CI_LOCAL_TABLE_SCANS_SENT, 1);
+ }
+
+ ndbrequire(refToNode(ref) == getOwnNodeId());
+ sendSignal(ref, GSN_SCAN_FRAGREQ, signal,
+ NDB_ARRAY_SIZE(treeNodePtr.p->m_scanfrag_data.m_scanFragReq),
+ JBB, &handle);
+
+ scanFragHandlePtr.p->m_state = ScanFragHandle::SFH_SCANNING;
+ treeNodePtr.p->m_scanfrag_data.m_rows_received = 0;
+ treeNodePtr.p->m_scanfrag_data.m_rows_expecting = ~Uint32(0);
+}
+
+void
+Dbspj::scanFrag_execTRANSID_AI(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr,
+ const RowPtr & rowRef)
+{
+ jam();
+ treeNodePtr.p->m_scanfrag_data.m_rows_received++;
+
+ LocalArenaPoolImpl pool(requestPtr.p->m_arena, m_dependency_map_pool);
+ Local_dependency_map list(pool, treeNodePtr.p->m_dependent_nodes);
+ Dependency_map::ConstDataBufferIterator it;
+
+ {
+ for (list.first(it); !it.isNull(); list.next(it))
+ {
+ jam();
+ Ptr<TreeNode> childPtr;
+ m_treenode_pool.getPtr(childPtr, * it.data);
+ ndbrequire(childPtr.p->m_info != 0&&childPtr.p->m_info->m_parent_row!=0);
+ (this->*(childPtr.p->m_info->m_parent_row))(signal,
+ requestPtr, childPtr,rowRef);
+ }
+ }
+
+ if (treeNodePtr.p->m_scanfrag_data.m_rows_received ==
+ treeNodePtr.p->m_scanfrag_data.m_rows_expecting)
+ {
+ jam();
+
+ if (treeNodePtr.p->m_bits & TreeNode::T_REPORT_BATCH_COMPLETE)
+ {
+ jam();
+ reportBatchComplete(signal, requestPtr, treeNodePtr);
+ }
+
+ checkBatchComplete(signal, requestPtr, 1);
+ return;
+ }
+}
+
+void
+Dbspj::scanFrag_execSCAN_FRAGREF(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr,
+ Ptr<ScanFragHandle> scanFragHandlePtr)
+{
+ const ScanFragRef* rep =
+ reinterpret_cast<const ScanFragRef*>(signal->getDataPtr());
+ Uint32 errCode = rep->errorCode;
+
+ DEBUG("scanFrag_execSCAN_FRAGREF, rep->senderData:" << rep->senderData
+ << ", requestPtr.p->m_senderData:" << requestPtr.p->m_senderData);
+ scanFragHandlePtr.p->m_state = ScanFragHandle::SFH_COMPLETE;
+ ndbrequire(treeNodePtr.p->m_state == TreeNode::TN_ACTIVE);
+ ndbrequire(requestPtr.p->m_cnt_active);
+ requestPtr.p->m_cnt_active--;
+ ndbrequire(requestPtr.p->m_outstanding);
+ requestPtr.p->m_outstanding--;
+ treeNodePtr.p->m_state = TreeNode::TN_INACTIVE;
+ mark_active(requestPtr, treeNodePtr, false);
+
+ abort(signal, requestPtr, errCode);
+}
+
+
+void
+Dbspj::scanFrag_execSCAN_FRAGCONF(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr,
+ Ptr<ScanFragHandle> scanFragHandlePtr)
+{
+ const ScanFragConf * conf =
+ reinterpret_cast<const ScanFragConf*>(signal->getDataPtr());
+ Uint32 rows = conf->completedOps;
+ Uint32 done = conf->fragmentCompleted;
+
+ Uint32 state = scanFragHandlePtr.p->m_state;
+ if (state == ScanFragHandle::SFH_WAIT_CLOSE && done == 0)
+ {
+ jam();
+ /**
+ * We sent an explicit close request...ignore this...a close will come later
+ */
+ return;
+ }
+
+ ndbrequire(done <= 2); // 0, 1, 2 (=ZSCAN_FRAG_CLOSED)
+
+ ndbassert(treeNodePtr.p->m_scanfrag_data.m_rows_expecting == ~Uint32(0));
+ treeNodePtr.p->m_scanfrag_data.m_rows_expecting = rows;
+ if (treeNodePtr.p->isLeaf())
+ {
+ /**
+ * If this is a leaf node, then no rows will be sent to the SPJ block,
+ * as there are no child operations to instantiate.
+ */
+ treeNodePtr.p->m_scanfrag_data.m_rows_received = rows;
+ }
+
+ requestPtr.p->m_rows += rows;
+ if (done)
+ {
+ jam();
+
+ ndbrequire(requestPtr.p->m_cnt_active);
+ requestPtr.p->m_cnt_active--;
+ treeNodePtr.p->m_state = TreeNode::TN_INACTIVE;
+ mark_active(requestPtr, treeNodePtr, false);
+ scanFragHandlePtr.p->m_state = ScanFragHandle::SFH_COMPLETE;
+ }
+ else
+ {
+ jam();
+ scanFragHandlePtr.p->m_state = ScanFragHandle::SFH_WAIT_NEXTREQ;
+ }
+
+ if (treeNodePtr.p->m_scanfrag_data.m_rows_expecting ==
+ treeNodePtr.p->m_scanfrag_data.m_rows_received ||
+ (state == ScanFragHandle::SFH_WAIT_CLOSE))
+ {
+ jam();
+
+ if (treeNodePtr.p->m_bits & TreeNode::T_REPORT_BATCH_COMPLETE)
+ {
+ jam();
+ reportBatchComplete(signal, requestPtr, treeNodePtr);
+ }
+
+ checkBatchComplete(signal, requestPtr, 1);
+ return;
+ }
+}
+
+void
+Dbspj::scanFrag_execSCAN_NEXTREQ(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ jamEntry();
+
+ Ptr<ScanFragHandle> scanFragHandlePtr;
+ m_scanfraghandle_pool.getPtr(scanFragHandlePtr, treeNodePtr.p->
+ m_scanfrag_data.m_scanFragHandlePtrI);
+
+ const ScanFragReq * org =
+ (ScanFragReq*)treeNodePtr.p->m_scanfrag_data.m_scanFragReq;
+
+ ScanFragNextReq* req =
+ reinterpret_cast<ScanFragNextReq*>(signal->getDataPtrSend());
+ req->senderData = treeNodePtr.p->m_scanfrag_data.m_scanFragHandlePtrI;
+ req->requestInfo = 0;
+ req->transId1 = requestPtr.p->m_transId[0];
+ req->transId2 = requestPtr.p->m_transId[1];
+ req->batch_size_rows = org->batch_size_rows;
+ req->batch_size_bytes = org->batch_size_bytes;
+
+ DEBUG("scanFrag_execSCAN_NEXTREQ to: " << hex << treeNodePtr.p->m_send.m_ref
+ << ", senderData: " << req->senderData);
+#ifdef DEBUG_SCAN_FRAGREQ
+ printSCANFRAGNEXTREQ(stdout, &signal->theData[0],
+ ScanFragNextReq::SignalLength, DBLQH);
+#endif
+
+ sendSignal(treeNodePtr.p->m_send.m_ref,
+ GSN_SCAN_NEXTREQ,
+ signal,
+ ScanFragNextReq::SignalLength,
+ JBB);
+
+ treeNodePtr.p->m_scanfrag_data.m_rows_received = 0;
+ treeNodePtr.p->m_scanfrag_data.m_rows_expecting = ~Uint32(0);
+ requestPtr.p->m_outstanding++;
+ scanFragHandlePtr.p->m_state = ScanFragHandle::SFH_SCANNING;
+}//Dbspj::scanFrag_execSCAN_NEXTREQ()
+
+void
+Dbspj::scanFrag_abort(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ jam();
+
+ Ptr<ScanFragHandle> scanFragHandlePtr;
+ m_scanfraghandle_pool.getPtr(scanFragHandlePtr, treeNodePtr.p->
+ m_scanfrag_data.m_scanFragHandlePtrI);
+ if (treeNodePtr.p->m_state == TreeNode::TN_ACTIVE)
+ {
+ jam();
+
+ switch(scanFragHandlePtr.p->m_state){
+ case ScanFragHandle::SFH_NOT_STARTED:
+ case ScanFragHandle::SFH_COMPLETE:
+ ndbrequire(false); // we shouldnt be TN_ACTIVE then...
+
+ case ScanFragHandle::SFH_WAIT_CLOSE:
+ jam();
+ // close already sent
+ return;
+ case ScanFragHandle::SFH_WAIT_NEXTREQ:
+ jam();
+ // we were idle
+ requestPtr.p->m_outstanding++;
+ break;
+ case ScanFragHandle::SFH_SCANNING:
+ jam();
+ break;
+ }
+
+ treeNodePtr.p->m_scanfrag_data.m_rows_expecting = ~Uint32(0);
+ scanFragHandlePtr.p->m_state = ScanFragHandle::SFH_WAIT_CLOSE;
+
+ ScanFragNextReq* req =
+ reinterpret_cast<ScanFragNextReq*>(signal->getDataPtrSend());
+ req->senderData = treeNodePtr.p->m_scanfrag_data.m_scanFragHandlePtrI;
+ req->requestInfo = ScanFragNextReq::ZCLOSE;
+ req->transId1 = requestPtr.p->m_transId[0];
+ req->transId2 = requestPtr.p->m_transId[1];
+ req->batch_size_rows = 0;
+ req->batch_size_bytes = 0;
+
+ sendSignal(treeNodePtr.p->m_send.m_ref,
+ GSN_SCAN_NEXTREQ,
+ signal,
+ ScanFragNextReq::SignalLength,
+ JBB);
+ }
+}
+
+
+void
+Dbspj::scanFrag_cleanup(Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ Uint32 ptrI = treeNodePtr.p->m_scanfrag_data.m_scanFragHandlePtrI;
+ if (ptrI != RNIL)
+ {
+ m_scanfraghandle_pool.release(ptrI);
+ }
+ cleanup_common(requestPtr, treeNodePtr);
+}
+
+/**
+ * END - MODULE SCAN FRAG
+ */
+
+/**
+ * MODULE SCAN INDEX
+ *
+ * NOTE: This may not be root-node
+ */
+const Dbspj::OpInfo
+Dbspj::g_ScanIndexOpInfo =
+{
+ &Dbspj::scanIndex_build,
+ &Dbspj::scanIndex_prepare,
+ 0, // start
+ &Dbspj::scanIndex_execTRANSID_AI,
+ 0, // execLQHKEYREF
+ 0, // execLQHKEYCONF
+ &Dbspj::scanIndex_execSCAN_FRAGREF,
+ &Dbspj::scanIndex_execSCAN_FRAGCONF,
+ &Dbspj::scanIndex_parent_row,
+ &Dbspj::scanIndex_parent_batch_complete,
+ &Dbspj::scanIndex_execSCAN_NEXTREQ,
+ &Dbspj::scanIndex_complete,
+ &Dbspj::scanIndex_abort,
+ &Dbspj::scanIndex_execNODE_FAILREP,
+ &Dbspj::scanIndex_cleanup
+};
+
+Uint32
+Dbspj::scanIndex_build(Build_context& ctx,
+ Ptr<Request> requestPtr,
+ const QueryNode* qn,
+ const QueryNodeParameters* qp)
+{
+ Uint32 err = 0;
+ Ptr<TreeNode> treeNodePtr;
+ const QN_ScanIndexNode * node = (const QN_ScanIndexNode*)qn;
+ const QN_ScanIndexParameters * param = (const QN_ScanIndexParameters*)qp;
+
+ do
+ {
+ err = createNode(ctx, requestPtr, treeNodePtr);
+ if (unlikely(err != 0))
+ break;
+
+ Uint32 batchSize = param->batchSize;
+
+ requestPtr.p->m_bits |= Request::RT_SCAN;
+ requestPtr.p->m_bits |= Request::RT_NEED_PREPARE;
+ requestPtr.p->m_bits |= Request::RT_NEED_COMPLETE;
+ treeNodePtr.p->m_info = &g_ScanIndexOpInfo;
+ treeNodePtr.p->m_bits |= TreeNode::T_ATTR_INTERPRETED;
+ treeNodePtr.p->m_bits |= TreeNode::T_NEED_REPORT_BATCH_COMPLETED;
+ treeNodePtr.p->m_batch_size = batchSize & 0xFFFF;
+
+ ScanFragReq*dst=(ScanFragReq*)treeNodePtr.p->m_scanindex_data.m_scanFragReq;
+ dst->senderData = treeNodePtr.i;
+ dst->resultRef = reference();
+ dst->resultData = treeNodePtr.i;
+ dst->savePointId = ctx.m_savepointId;
+ dst->batch_size_rows = batchSize & 0xFFFF;
+ dst->batch_size_bytes = batchSize >> 16;
+
+ Uint32 transId1 = requestPtr.p->m_transId[0];
+ Uint32 transId2 = requestPtr.p->m_transId[1];
+ dst->transId1 = transId1;
+ dst->transId2 = transId2;
+
+ Uint32 treeBits = node->requestInfo;
+ Uint32 paramBits = param->requestInfo;
+ Uint32 requestInfo = 0;
+ ScanFragReq::setRangeScanFlag(requestInfo, 1);
+ ScanFragReq::setReadCommittedFlag(requestInfo, 1);
+ ScanFragReq::setScanPrio(requestInfo, ctx.m_scanPrio);
+ ScanFragReq::setNoDiskFlag(requestInfo,
+ (treeBits & DABits::NI_LINKED_DISK) == 0 &&
+ (paramBits & DABits::PI_DISK_ATTR) == 0);
+ ScanFragReq::setCorrFactorFlag(requestInfo, 1);
+ dst->requestInfo = requestInfo;
+
+ err = DbspjErr::InvalidTreeNodeSpecification;
+ DEBUG("scanIndex_build: len=" << node->len);
+ if (unlikely(node->len < QN_ScanIndexNode::NodeSize))
+ break;
+
+ dst->tableId = node->tableId;
+ dst->schemaVersion = node->tableVersion;
+
+ err = DbspjErr::InvalidTreeParametersSpecification;
+ DEBUG("param len: " << param->len);
+ if (unlikely(param->len < QN_ScanIndexParameters::NodeSize))
+ {
+ jam();
+ DEBUG_CRASH();
+ break;
+ }
+
+ ctx.m_resultData = param->resultData;
+
+ /**
+ * Parse stuff
+ */
+ struct DABuffer nodeDA, paramDA;
+ nodeDA.ptr = node->optional;
+ nodeDA.end = nodeDA.ptr + (node->len - QN_ScanIndexNode::NodeSize);
+ paramDA.ptr = param->optional;
+ paramDA.end = paramDA.ptr + (param->len - QN_ScanIndexParameters::NodeSize);
+
+ err = parseScanIndex(ctx, requestPtr, treeNodePtr,
+ nodeDA, treeBits, paramDA, paramBits);
+
+ if (unlikely(err != 0))
+ {
+ jam();
+ DEBUG_CRASH();
+ break;
+ }
+
+ /**
+ * Since we T_NEED_REPORT_BATCH_COMPLETED, we set
+ * this on all our parents...
+ */
+ Ptr<TreeNode> nodePtr;
+ nodePtr.i = treeNodePtr.p->m_parentPtrI;
+ while (nodePtr.i != RNIL)
+ {
+ jam();
+ m_treenode_pool.getPtr(nodePtr);
+ nodePtr.p->m_bits |= TreeNode::T_REPORT_BATCH_COMPLETE;
+ nodePtr.p->m_bits |= TreeNode::T_NEED_REPORT_BATCH_COMPLETED;
+ nodePtr.i = nodePtr.p->m_parentPtrI;
+ }
+
+ ctx.m_scan_cnt++;
+ /**
+ * In the scenario with only 1 scan in tree,
+ * register cursor here, so we don't need to search for in after build
+ * If m_scan_cnt > 1,
+ * then this list will simply be cleared after build
+ */
+ registerCursor(requestPtr, treeNodePtr);
+
+ return 0;
+ } while (0);
+
+ return err;
+}
+
+Uint32
+Dbspj::parseScanIndex(Build_context& ctx,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr,
+ DABuffer tree, Uint32 treeBits,
+ DABuffer param, Uint32 paramBits)
+{
+ Uint32 err = 0;
+
+ typedef QN_ScanIndexNode Node;
+ typedef QN_ScanIndexParameters Params;
+
+ do
+ {
+ jam();
+
+ ScanIndexData& data = treeNodePtr.p->m_scanindex_data;
+ data.m_fragments.init();
+ data.m_frags_outstanding = 0;
+ data.m_frags_not_complete = 0;
+
+ err = parseDA(ctx, requestPtr, treeNodePtr,
+ tree, treeBits, param, paramBits);
+ if (unlikely(err != 0))
+ break;
+
+ if (treeBits & Node::SI_PRUNE_PATTERN)
+ {
+ Uint32 len_cnt = * tree.ptr ++;
+ Uint32 len = len_cnt & 0xFFFF; // length of pattern in words
+ Uint32 cnt = len_cnt >> 16; // no of parameters
+
+ LocalArenaPoolImpl pool(requestPtr.p->m_arena, m_dependency_map_pool);
+ ndbrequire((cnt==0) == ((treeBits & Node::SI_PRUNE_PARAMS) ==0));
+ ndbrequire((cnt==0) == ((paramBits & Params::SIP_PRUNE_PARAMS)==0));
+
+ if (treeBits & Node::SI_PRUNE_LINKED)
+ {
+ jam();
+ DEBUG("LINKED-PRUNE PATTERN w/ " << cnt << " PARAM values");
+
+ data.m_prunePattern.init();
+ Local_pattern_store pattern(pool, data.m_prunePattern);
+
+ /**
+ * Expand pattern into a new pattern (with linked values)
+ */
+ err = expand(pattern, treeNodePtr, tree, len, param, cnt);
+ if (unlikely(err != 0))
+ break;
+
+ treeNodePtr.p->m_bits |= TreeNode::T_PRUNE_PATTERN;
+ c_Counters.incr_counter(CI_PRUNED_RANGE_SCANS_RECEIVED, 1);
+ }
+ else
+ {
+ jam();
+ DEBUG("FIXED-PRUNE w/ " << cnt << " PARAM values");
+
+ /**
+ * Expand pattern directly into
+ * This means a "fixed" pruning from here on
+ * i.e guaranteed single partition
+ */
+ Uint32 prunePtrI = RNIL;
+ bool hasNull;
+ err = expand(prunePtrI, tree, len, param, cnt, hasNull);
+ if (unlikely(err != 0))
+ break;
+
+ if (unlikely(hasNull))
+ {
+ /* API should have elliminated requests w/ const-NULL keys */
+ jam();
+ DEBUG("BEWARE: T_CONST_PRUNE-key contain NULL values");
+// treeNodePtr.p->m_bits |= TreeNode::T_NULL_PRUNE;
+// break;
+ ndbrequire(false);
+ }
+ ndbrequire(prunePtrI != RNIL); /* todo: can we allow / take advantage of NULLs in range scan? */
+ data.m_constPrunePtrI = prunePtrI;
+
+ /**
+ * We may not compute the partition for the hash-key here
+ * as we have not yet opened a read-view
+ */
+ treeNodePtr.p->m_bits |= TreeNode::T_CONST_PRUNE;
+ c_Counters.incr_counter(CI_CONST_PRUNED_RANGE_SCANS_RECEIVED, 1);
+ }
+ } //SI_PRUNE_PATTERN
+
+ if ((treeNodePtr.p->m_bits & TreeNode::T_CONST_PRUNE) == 0 &&
+ ((treeBits & Node::SI_PARALLEL) ||
+ ((paramBits & Params::SIP_PARALLEL))))
+ {
+ jam();
+ treeNodePtr.p->m_bits |= TreeNode::T_SCAN_PARALLEL;
+ }
+
+ return 0;
+ } while(0);
+
+ DEBUG_CRASH();
+ return err;
+}
+
+void
+Dbspj::scanIndex_prepare(Signal * signal,
+ Ptr<Request> requestPtr, Ptr<TreeNode> treeNodePtr)
+{
+ jam();
+
+ treeNodePtr.p->m_state = TreeNode::TN_PREPARING;
+ ScanFragReq*dst=(ScanFragReq*)treeNodePtr.p->m_scanindex_data.m_scanFragReq;
+
+ DihScanTabReq * req = (DihScanTabReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->senderData = treeNodePtr.i;
+ req->tableId = dst->tableId;
+ req->schemaTransId = 0;
+ sendSignal(DBDIH_REF, GSN_DIH_SCAN_TAB_REQ, signal,
+ DihScanTabReq::SignalLength, JBB);
+
+ requestPtr.p->m_outstanding++;
+}
+
+void
+Dbspj::execDIH_SCAN_TAB_REF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(false);
+}
+
+void
+Dbspj::execDIH_SCAN_TAB_CONF(Signal* signal)
+{
+ jamEntry();
+ DihScanTabConf * conf = (DihScanTabConf*)signal->getDataPtr();
+
+ Ptr<TreeNode> treeNodePtr;
+ m_treenode_pool.getPtr(treeNodePtr, conf->senderData);
+ ndbrequire(treeNodePtr.p->m_info == &g_ScanIndexOpInfo);
+
+ ScanIndexData& data = treeNodePtr.p->m_scanindex_data;
+
+ Uint32 cookie = conf->scanCookie;
+ Uint32 fragCount = conf->fragmentCount;
+ ScanFragReq * dst = (ScanFragReq*)data.m_scanFragReq;
+
+ if (conf->reorgFlag)
+ {
+ jam();
+ ScanFragReq::setReorgFlag(dst->requestInfo, 1);
+ }
+
+ data.m_fragCount = fragCount;
+ data.m_scanCookie = cookie;
+
+ const Uint32 prunemask = TreeNode::T_PRUNE_PATTERN | TreeNode::T_CONST_PRUNE;
+ bool pruned = (treeNodePtr.p->m_bits & prunemask) != 0;
+
+ Ptr<Request> requestPtr;
+ m_request_pool.getPtr(requestPtr, treeNodePtr.p->m_requestPtrI);
+
+ Ptr<ScanFragHandle> fragPtr;
+ Local_ScanFragHandle_list list(m_scanfraghandle_pool, data.m_fragments);
+ if (likely(m_scanfraghandle_pool.seize(requestPtr.p->m_arena, fragPtr)))
+ {
+ jam();
+ fragPtr.p->init(0);
+ fragPtr.p->m_treeNodePtrI = treeNodePtr.i;
+ list.addLast(fragPtr);
+ }
+ else
+ {
+ jam();
+ goto error1;
+ }
+
+ if (treeNodePtr.p->m_bits & TreeNode::T_CONST_PRUNE)
+ {
+ jam();
+
+ // TODO we need a different variant of computeHash here,
+ // since m_constPrunePtrI does not contain full primary key
+ // but only parts in distribution key
+
+ BuildKeyReq tmp;
+ Uint32 indexId = dst->tableId;
+ Uint32 tableId = g_key_descriptor_pool.getPtr(indexId)->primaryTableId;
+ Uint32 err = computePartitionHash(signal, tmp, tableId, data.m_constPrunePtrI);
+ if (unlikely(err != 0))
+ goto error;
+
+ releaseSection(data.m_constPrunePtrI);
+ data.m_constPrunePtrI = RNIL;
+
+ err = getNodes(signal, tmp, tableId);
+ if (unlikely(err != 0))
+ goto error;
+
+ fragPtr.p->m_fragId = tmp.fragId;
+ fragPtr.p->m_ref = tmp.receiverRef;
+ data.m_fragCount = 1;
+ }
+ else if (fragCount == 1)
+ {
+ jam();
+ /**
+ * This is roughly equivalent to T_CONST_PRUNE
+ * pretend that it is const-pruned
+ */
+ if (treeNodePtr.p->m_bits & TreeNode::T_PRUNE_PATTERN)
+ {
+ jam();
+ LocalArenaPoolImpl pool(requestPtr.p->m_arena, m_dependency_map_pool);
+ Local_pattern_store pattern(pool, data.m_prunePattern);
+ pattern.release();
+ }
+ data.m_constPrunePtrI = RNIL;
+ Uint32 clear = TreeNode::T_PRUNE_PATTERN | TreeNode::T_SCAN_PARALLEL;
+ treeNodePtr.p->m_bits &= ~clear;
+ treeNodePtr.p->m_bits |= TreeNode::T_CONST_PRUNE;
+
+ /**
+ * We must get fragPtr.p->m_ref...so set pruned=false
+ */
+ pruned = false;
+ }
+ else
+ {
+ for (Uint32 i = 1; i<fragCount; i++)
+ {
+ jam();
+ Ptr<ScanFragHandle> fragPtr;
+ if (likely(m_scanfraghandle_pool.seize(requestPtr.p->m_arena, fragPtr)))
+ {
+ jam();
+ fragPtr.p->init(i);
+ fragPtr.p->m_treeNodePtrI = treeNodePtr.i;
+ list.addLast(fragPtr);
+ }
+ else
+ {
+ goto error1;
+ }
+ }
+ }
+
+ if (!pruned)
+ {
+ jam();
+ Uint32 tableId = ((ScanFragReq*)data.m_scanFragReq)->tableId;
+ DihScanGetNodesReq * req = (DihScanGetNodesReq*)signal->getDataPtrSend();
+ req->senderRef = reference();
+ req->tableId = tableId;
+ req->scanCookie = cookie;
+
+ Uint32 cnt = 0;
+ for (list.first(fragPtr); !fragPtr.isNull(); list.next(fragPtr))
+ {
+ jam();
+ req->senderData = fragPtr.i;
+ req->fragId = fragPtr.p->m_fragId;
+ sendSignal(DBDIH_REF, GSN_DIH_SCAN_GET_NODES_REQ, signal,
+ DihScanGetNodesReq::SignalLength, JBB);
+ cnt++;
+ }
+ data.m_frags_outstanding = cnt;
+ requestPtr.p->m_outstanding++;
+ }
+ else
+ {
+ jam();
+ treeNodePtr.p->m_state = TreeNode::TN_INACTIVE;
+ }
+ checkPrepareComplete(signal, requestPtr, 1);
+
+ return;
+
+error1:
+error:
+ ndbrequire(false);
+}
+
+void
+Dbspj::execDIH_SCAN_GET_NODES_REF(Signal* signal)
+{
+ jamEntry();
+ ndbrequire(false);
+}
+
+void
+Dbspj::execDIH_SCAN_GET_NODES_CONF(Signal* signal)
+{
+ jamEntry();
+
+ DihScanGetNodesConf * conf = (DihScanGetNodesConf*)signal->getDataPtr();
+
+ Uint32 senderData = conf->senderData;
+ Uint32 node = conf->nodes[0];
+ Uint32 instanceKey = conf->instanceKey;
+
+ Ptr<ScanFragHandle> fragPtr;
+ m_scanfraghandle_pool.getPtr(fragPtr, senderData);
+ Ptr<TreeNode> treeNodePtr;
+ m_treenode_pool.getPtr(treeNodePtr, fragPtr.p->m_treeNodePtrI);
+ ndbrequire(treeNodePtr.p->m_info == &g_ScanIndexOpInfo);
+ ScanIndexData& data = treeNodePtr.p->m_scanindex_data;
+ ndbrequire(data.m_frags_outstanding > 0);
+ data.m_frags_outstanding--;
+
+ fragPtr.p->m_ref = numberToRef(DBLQH, instanceKey, node);
+
+ if (data.m_frags_outstanding == 0)
+ {
+ jam();
+
+ treeNodePtr.p->m_state = TreeNode::TN_INACTIVE;
+
+ Ptr<Request> requestPtr;
+ m_request_pool.getPtr(requestPtr, treeNodePtr.p->m_requestPtrI);
+ checkPrepareComplete(signal, requestPtr, 1);
+ }
+}
+
+Uint32
+Dbspj::scanIndex_findFrag(Local_ScanFragHandle_list & list,
+ Ptr<ScanFragHandle> & fragPtr, Uint32 fragId)
+{
+ for (list.first(fragPtr); !fragPtr.isNull(); list.next(fragPtr))
+ {
+ jam();
+ if (fragPtr.p->m_fragId == fragId)
+ {
+ jam();
+ return 0;
+ }
+ }
+
+ return 99; // TODO
+}
+
+void
+Dbspj::scanIndex_parent_row(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr,
+ const RowPtr & rowRef)
+{
+ jam();
+
+ Uint32 err;
+ ScanIndexData& data = treeNodePtr.p->m_scanindex_data;
+
+ /**
+ * Construct range definition,
+ * and if prune pattern enabled
+ * stuff it onto correct scanindexFrag
+ */
+ do
+ {
+ Ptr<ScanFragHandle> fragPtr;
+ Local_ScanFragHandle_list list(m_scanfraghandle_pool, data.m_fragments);
+ LocalArenaPoolImpl pool(requestPtr.p->m_arena, m_dependency_map_pool);
+ if (treeNodePtr.p->m_bits & TreeNode::T_PRUNE_PATTERN)
+ {
+ jam();
+
+ /**
+ * TODO: Expand into linear memory instead
+ * of expanding into sections, and then copy
+ * section into linear
+ */
+ Local_pattern_store pattern(pool, data.m_prunePattern);
+ Uint32 pruneKeyPtrI = RNIL;
+ bool hasNull;
+ err = expand(pruneKeyPtrI, pattern, rowRef, hasNull);
+ if (unlikely(err != 0))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+
+ if (unlikely(hasNull))
+ {
+ jam();
+ DEBUG("T_PRUNE_PATTERN-key contain NULL values");
+
+ // Ignore this request as 'NULL == <column>' will never give a match
+ if (pruneKeyPtrI != RNIL)
+ {
+ releaseSection(pruneKeyPtrI);
+ }
+ return; // Bailout, SCANREQ would have returned 0 rows anyway
+ }
+
+ // TODO we need a different variant of computeHash here,
+ // since pruneKeyPtrI does not contain full primary key
+ // but only parts in distribution key
+
+ BuildKeyReq tmp;
+ ScanFragReq * dst = (ScanFragReq*)data.m_scanFragReq;
+ Uint32 indexId = dst->tableId;
+ Uint32 tableId = g_key_descriptor_pool.getPtr(indexId)->primaryTableId;
+ err = computePartitionHash(signal, tmp, tableId, pruneKeyPtrI);
+ releaseSection(pruneKeyPtrI); // see ^ TODO
+ if (unlikely(err != 0))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+
+ err = getNodes(signal, tmp, tableId);
+ if (unlikely(err != 0))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+
+ err = scanIndex_findFrag(list, fragPtr, tmp.fragId);
+ if (unlikely(err != 0))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+
+ if (fragPtr.p->m_ref == 0)
+ {
+ jam();
+ fragPtr.p->m_ref = tmp.receiverRef;
+ }
+ else
+ {
+ /**
+ * TODO: not 100% sure if this is correct with reorg ongoing...
+ * but scanning "old" should regardless be safe as we still have
+ * scanCookie
+ */
+ ndbassert(fragPtr.p->m_ref == tmp.receiverRef);
+ }
+ }
+ else
+ {
+ jam();
+ /**
+ * If const prune, or no-prune, store on first fragment,
+ * and send to 1 or all resp.
+ */
+ list.first(fragPtr);
+ }
+
+ Uint32 ptrI = fragPtr.p->m_rangePtrI;
+ if (ptrI == RNIL)
+ {
+ jam();
+ data.m_frags_not_complete++;
+ }
+
+ bool hasNull;
+ if (treeNodePtr.p->m_bits & TreeNode::T_KEYINFO_CONSTRUCTED)
+ {
+ jam();
+ Local_pattern_store pattern(pool, treeNodePtr.p->m_keyPattern);
+ err = expand(ptrI, pattern, rowRef, hasNull);
+ if (unlikely(err != 0))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+ }
+ else
+ {
+ jam();
+ // Fixed key...fix later...
+ ndbrequire(false);
+ }
+// ndbrequire(!hasNull); // FIXME, can't ignore request as we already added it to keyPattern
+ fragPtr.p->m_rangePtrI = ptrI;
+ scanIndex_fixupBound(fragPtr, ptrI, rowRef.m_src_correlation);
+
+ if (treeNodePtr.p->m_bits & TreeNode::T_ONE_SHOT)
+ {
+ jam();
+ /**
+ * We being a T_ONE_SHOT means that we're only be called
+ * with parent_row once, i.e batch is complete
+ */
+ scanIndex_parent_batch_complete(signal, requestPtr, treeNodePtr);
+ }
+
+ return;
+ } while (0);
+
+ ndbrequire(false);
+}
+
+
+void
+Dbspj::scanIndex_fixupBound(Ptr<ScanFragHandle> fragPtr,
+ Uint32 ptrI, Uint32 corrVal)
+{
+ /**
+ * Index bounds...need special tender and care...
+ *
+ * 1) Set #bound no, bound-size, and renumber attributes
+ */
+ SectionReader r0(ptrI, getSectionSegmentPool());
+ ndbrequire(r0.step(fragPtr.p->m_range_builder.m_range_size));
+ Uint32 boundsz = r0.getSize() - fragPtr.p->m_range_builder.m_range_size;
+ Uint32 boundno = fragPtr.p->m_range_builder.m_range_cnt + 1;
+
+ Uint32 tmp;
+ ndbrequire(r0.peekWord(&tmp));
+ tmp |= (boundsz << 16) | ((corrVal & 0xFFF) << 4);
+ ndbrequire(r0.updateWord(tmp));
+ ndbrequire(r0.step(1)); // Skip first BoundType
+
+ // TODO: Renumbering below assume there are only EQ-bounds !!
+ Uint32 id = 0;
+ Uint32 len32;
+ do
+ {
+ ndbrequire(r0.peekWord(&tmp));
+ AttributeHeader ah(tmp);
+ Uint32 len = ah.getByteSize();
+ AttributeHeader::init(&tmp, id++, len);
+ ndbrequire(r0.updateWord(tmp));
+ len32 = (len + 3) >> 2;
+ } while (r0.step(2 + len32)); // Skip AttributeHeader(1) + Attribute(len32) + next BoundType(1)
+
+ fragPtr.p->m_range_builder.m_range_cnt = boundno;
+ fragPtr.p->m_range_builder.m_range_size = r0.getSize();
+}
+
+void
+Dbspj::scanIndex_parent_batch_complete(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ jam();
+
+ ScanIndexData& data = treeNodePtr.p->m_scanindex_data;
+ data.m_rows_received = 0;
+ data.m_rows_expecting = 0;
+ ndbassert(data.m_frags_outstanding == 0);
+
+ if (data.m_frags_not_complete == 0)
+ {
+ jam();
+ /**
+ * No keys was produced...
+ */
+ return;
+ }
+ else if ((treeNodePtr.p->m_bits & TreeNode::T_PRUNE_PATTERN) == 0)
+ {
+ jam();
+ data.m_frags_not_complete = data.m_fragCount;
+ }
+
+ /**
+ * When parent's batch is complete, we send our batch
+ */
+ scanIndex_send(signal, requestPtr, treeNodePtr);
+}
+
+void
+Dbspj::scanIndex_send(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ jam();
+
+ ScanIndexData& data = treeNodePtr.p->m_scanindex_data;
+ const ScanFragReq * org = (const ScanFragReq*)data.m_scanFragReq;
+
+ Uint32 cnt = 1;
+ Uint32 bs_rows = org->batch_size_rows;
+ Uint32 bs_bytes = org->batch_size_bytes;
+ if (treeNodePtr.p->m_bits & TreeNode::T_SCAN_PARALLEL)
+ {
+ jam();
+ cnt = data.m_frags_not_complete;
+ ndbrequire(cnt > 0);
+
+ bs_rows /= cnt;
+ bs_bytes /= cnt;
+ ndbassert(bs_rows > 0);
+ }
+
+ /**
+ * keys,
+ * - sliced out to each ScanFragHandle => release = true
+ * - all kept on first ScanFragHandle => release = false
+ */
+ Uint32 prunemask = TreeNode::T_PRUNE_PATTERN | TreeNode::T_CONST_PRUNE;
+ bool release = (treeNodePtr.p->m_bits & prunemask) != 0;
+
+ ScanFragReq* req = reinterpret_cast<ScanFragReq*>(signal->getDataPtrSend());
+ memcpy(req, org, sizeof(data.m_scanFragReq));
+ // req->variableData[0] // set below
+ req->variableData[1] = requestPtr.p->m_rootResultData;
+ req->batch_size_bytes = bs_bytes;
+ req->batch_size_rows = bs_rows;
+
+ Ptr<ScanFragHandle> fragPtr;
+ Local_ScanFragHandle_list list(m_scanfraghandle_pool, data.m_fragments);
+
+ Uint32 keyInfoPtrI;
+ if (release == false)
+ {
+ jam();
+ list.first(fragPtr);
+ keyInfoPtrI = fragPtr.p->m_rangePtrI;
+ ndbrequire(keyInfoPtrI != RNIL);
+ fragPtr.p->m_rangePtrI = RNIL;
+ }
+
+ Uint32 batchRange = 0;
+ list.first(fragPtr);
+ for (Uint32 i = 0; i < cnt && !fragPtr.isNull(); list.next(fragPtr))
+ {
+ jam();
+
+ SectionHandle handle(this);
+
+ Uint32 ref = fragPtr.p->m_ref;
+ Uint32 attrInfoPtrI = treeNodePtr.p->m_send.m_attrInfoPtrI;
+
+ /**
+ * Set data specific for this fragment
+ */
+ req->senderData = fragPtr.i;
+ req->fragmentNoKeyLen = fragPtr.p->m_fragId;
+
+ if (release)
+ {
+ jam();
+ keyInfoPtrI = fragPtr.p->m_rangePtrI;
+ if (keyInfoPtrI == RNIL)
+ {
+ jam();
+ fragPtr.p->m_state = ScanFragHandle::SFH_COMPLETE;
+ continue;
+ }
+ fragPtr.p->m_rangePtrI = RNIL;
+
+ /**
+ * If we'll use sendSignal() and we need to send the attrInfo several
+ * times, we need to copy them
+ */
+ Uint32 tmp = RNIL;
+ ndbrequire(dupSection(tmp, attrInfoPtrI)); // TODO handle error
+ attrInfoPtrI = tmp;
+ }
+ fragPtr.p->reset_ranges();
+
+ req->variableData[0] = batchRange;
+ getSection(handle.m_ptr[0], attrInfoPtrI);
+ getSection(handle.m_ptr[1], keyInfoPtrI);
+ handle.m_cnt = 2;
+
+#if defined DEBUG_SCAN_FRAGREQ
+ {
+ ndbout_c("SCAN_FRAGREQ to %x", ref);
+ printSCAN_FRAGREQ(stdout, signal->getDataPtrSend(),
+ NDB_ARRAY_SIZE(treeNodePtr.p->m_scanfrag_data.m_scanFragReq),
+ DBLQH);
+ printf("ATTRINFO: ");
+ print(handle.m_ptr[0], stdout);
+ printf("KEYINFO: ");
+ print(handle.m_ptr[1], stdout);
+ }
+#endif
+
+ if (refToNode(ref) == getOwnNodeId())
+ {
+ c_Counters.incr_counter(CI_LOCAL_RANGE_SCANS_SENT, 1);
+ }
+ else
+ {
+ c_Counters.incr_counter(CI_REMOTE_RANGE_SCANS_SENT, 1);
+ }
+
+ if (release)
+ {
+ jam();
+ sendSignal(ref, GSN_SCAN_FRAGREQ, signal,
+ NDB_ARRAY_SIZE(data.m_scanFragReq), JBB, &handle);
+ }
+ else
+ {
+ jam();
+ sendSignalNoRelease(ref, GSN_SCAN_FRAGREQ, signal,
+ NDB_ARRAY_SIZE(data.m_scanFragReq), JBB, &handle);
+ }
+ handle.clear();
+
+ i++;
+ fragPtr.p->m_state = ScanFragHandle::SFH_SCANNING; // running
+ data.m_frags_outstanding++;
+ batchRange += bs_rows;
+ }
+
+ if (release == false)
+ {
+ jam();
+ // only supported for now...
+ ndbrequire(treeNodePtr.p->m_bits & TreeNode::T_SCAN_PARALLEL);
+ releaseSection(keyInfoPtrI);
+ }
+
+ if (treeNodePtr.p->m_bits & TreeNode::T_SCAN_PARALLEL)
+ {
+ ndbrequire(data.m_frags_outstanding == data.m_frags_not_complete);
+ }
+ else
+ {
+ ndbrequire(data.m_frags_outstanding == 1);
+ }
+
+ requestPtr.p->m_cnt_active++;
+ requestPtr.p->m_outstanding++;
+ mark_active(requestPtr, treeNodePtr, true);
+ treeNodePtr.p->m_state = TreeNode::TN_ACTIVE;
+}
+
+void
+Dbspj::scanIndex_execTRANSID_AI(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr,
+ const RowPtr & rowRef)
+{
+ jam();
+
+ LocalArenaPoolImpl pool(requestPtr.p->m_arena, m_dependency_map_pool);
+ Local_dependency_map list(pool, treeNodePtr.p->m_dependent_nodes);
+ Dependency_map::ConstDataBufferIterator it;
+
+ {
+ for (list.first(it); !it.isNull(); list.next(it))
+ {
+ jam();
+ Ptr<TreeNode> childPtr;
+ m_treenode_pool.getPtr(childPtr, * it.data);
+ ndbrequire(childPtr.p->m_info != 0&&childPtr.p->m_info->m_parent_row!=0);
+ (this->*(childPtr.p->m_info->m_parent_row))(signal,
+ requestPtr, childPtr,rowRef);
+ }
+ }
+
+ ScanIndexData& data = treeNodePtr.p->m_scanindex_data;
+ data.m_rows_received++;
+
+ if (data.m_frags_outstanding == 0 &&
+ data.m_rows_received == data.m_rows_expecting)
+ {
+ jam();
+ /**
+ * Finished...
+ */
+ if (treeNodePtr.p->m_bits & TreeNode::T_REPORT_BATCH_COMPLETE)
+ {
+ jam();
+ reportBatchComplete(signal, requestPtr, treeNodePtr);
+ }
+
+ checkBatchComplete(signal, requestPtr, 1);
+ return;
+ }
+}
+
+void
+Dbspj::scanIndex_execSCAN_FRAGCONF(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr,
+ Ptr<ScanFragHandle> fragPtr)
+{
+ jam();
+
+ const ScanFragConf * conf = (const ScanFragConf*)(signal->getDataPtr());
+
+ Uint32 rows = conf->completedOps;
+ Uint32 done = conf->fragmentCompleted;
+
+ Uint32 state = fragPtr.p->m_state;
+ ScanIndexData& data = treeNodePtr.p->m_scanindex_data;
+
+ if (state == ScanFragHandle::SFH_WAIT_CLOSE && done == 0)
+ {
+ jam();
+ /**
+ * We sent an explicit close request...ignore this...a close will come later
+ */
+ return;
+ }
+
+ requestPtr.p->m_rows += rows;
+
+ if (!treeNodePtr.p->isLeaf())
+ {
+ jam();
+ data.m_rows_expecting += rows;
+ }
+ ndbrequire(data.m_frags_outstanding);
+ ndbrequire(state == ScanFragHandle::SFH_SCANNING ||
+ state == ScanFragHandle::SFH_WAIT_CLOSE);
+
+ data.m_frags_outstanding--;
+ fragPtr.p->m_state = ScanFragHandle::SFH_WAIT_NEXTREQ;
+
+ if (done)
+ {
+ jam();
+ fragPtr.p->m_state = ScanFragHandle::SFH_COMPLETE;
+ ndbrequire(data.m_frags_not_complete>0);
+ data.m_frags_not_complete--;
+
+ if (data.m_frags_not_complete == 0)
+ {
+ jam();
+ ndbrequire(requestPtr.p->m_cnt_active);
+ requestPtr.p->m_cnt_active--;
+ treeNodePtr.p->m_state = TreeNode::TN_INACTIVE;
+ mark_active(requestPtr, treeNodePtr, false);
+ }
+ }
+
+
+ if (data.m_frags_outstanding == 0)
+ {
+ /**
+ * Don't reportBatchComplete to children if we're aborting...
+ */
+ if (state == ScanFragHandle::SFH_WAIT_CLOSE)
+ {
+ jam();
+ ndbrequire((requestPtr.p->m_state & Request::RS_ABORTING) != 0);
+ }
+ else if (! (data.m_rows_received == data.m_rows_expecting))
+ {
+ jam();
+ return;
+ }
+ else
+ {
+ if (treeNodePtr.p->m_bits & TreeNode::T_REPORT_BATCH_COMPLETE)
+ {
+ jam();
+ reportBatchComplete(signal, requestPtr, treeNodePtr);
+ }
+ }
+
+ checkBatchComplete(signal, requestPtr, 1);
+ return;
+ }
+}
+
+void
+Dbspj::scanIndex_execSCAN_FRAGREF(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr,
+ Ptr<ScanFragHandle> fragPtr)
+{
+ jam();
+
+ const ScanFragRef * rep = CAST_CONSTPTR(ScanFragRef, signal->getDataPtr());
+ const Uint32 errCode = rep->errorCode;
+
+ Uint32 state = fragPtr.p->m_state;
+ ndbrequire(state == ScanFragHandle::SFH_SCANNING ||
+ state == ScanFragHandle::SFH_WAIT_CLOSE);
+
+ fragPtr.p->m_state = ScanFragHandle::SFH_COMPLETE;
+
+ ScanIndexData& data = treeNodePtr.p->m_scanindex_data;
+ ndbrequire(data.m_frags_not_complete > 0);
+ data.m_frags_not_complete--;
+ ndbrequire(data.m_frags_outstanding > 0);
+ data.m_frags_outstanding--;
+
+ if (data.m_frags_not_complete == 0)
+ {
+ jam();
+ ndbrequire(requestPtr.p->m_cnt_active);
+ requestPtr.p->m_cnt_active--;
+ treeNodePtr.p->m_state = TreeNode::TN_INACTIVE;
+ mark_active(requestPtr, treeNodePtr, false);
+ }
+
+ if (data.m_frags_outstanding == 0)
+ {
+ jam();
+ ndbrequire(requestPtr.p->m_outstanding);
+ requestPtr.p->m_outstanding--;
+ }
+
+ abort(signal, requestPtr, errCode);
+}
+
+void
+Dbspj::scanIndex_execSCAN_NEXTREQ(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ jam();
+
+ ScanIndexData& data = treeNodePtr.p->m_scanindex_data;
+
+ data.m_rows_received = 0;
+ data.m_rows_expecting = 0;
+ ndbassert(data.m_frags_outstanding == 0);
+
+ ndbrequire(data.m_frags_not_complete>0);
+ Uint32 cnt = data.m_frags_not_complete;
+ if ((treeNodePtr.p->m_bits & TreeNode::T_SCAN_PARALLEL) == 0)
+ {
+ jam();
+ cnt = 1;
+ }
+
+ const ScanFragReq * org = (const ScanFragReq*)data.m_scanFragReq;
+ const Uint32 bs_rows = org->batch_size_rows/cnt;
+ ndbassert(bs_rows > 0);
+ ScanFragNextReq* req =
+ reinterpret_cast<ScanFragNextReq*>(signal->getDataPtrSend());
+ req->requestInfo = 0;
+ ScanFragNextReq::setCorrFactorFlag(req->requestInfo);
+ req->transId1 = requestPtr.p->m_transId[0];
+ req->transId2 = requestPtr.p->m_transId[1];
+ req->batch_size_rows = bs_rows;
+ req->batch_size_bytes = org->batch_size_bytes/cnt;
+
+ Uint32 batchRange = 0;
+ Ptr<ScanFragHandle> fragPtr;
+ Local_ScanFragHandle_list list(m_scanfraghandle_pool, data.m_fragments);
+ list.first(fragPtr);
+ for (Uint32 i = 0; i < cnt && !fragPtr.isNull(); list.next(fragPtr))
+ {
+ jam();
+ if (fragPtr.p->m_state == ScanFragHandle::SFH_WAIT_NEXTREQ)
+ {
+ jam();
+
+ i++;
+ data.m_frags_outstanding++;
+ req->variableData[0] = batchRange;
+ fragPtr.p->m_state = ScanFragHandle::SFH_SCANNING;
+ batchRange += bs_rows;
+
+ DEBUG("scanIndex_execSCAN_NEXTREQ to: " << hex
+ << treeNodePtr.p->m_send.m_ref
+ << ", senderData: " << req->senderData);
+
+#ifdef DEBUG_SCAN_FRAGREQ
+ printSCANFRAGNEXTREQ(stdout, &signal->theData[0],
+ ScanFragNextReq:: SignalLength + 1, DBLQH);
+#endif
+
+ req->senderData = fragPtr.i;
+ sendSignal(fragPtr.p->m_ref, GSN_SCAN_NEXTREQ, signal,
+ ScanFragNextReq::SignalLength + 1,
+ JBB);
+ }
+ }
+
+ /**
+ * cursor should not have been positioned here...
+ * unless we actually had something more to send.
+ * so require that we did actually send something
+ */
+ ndbrequire(data.m_frags_outstanding > 0);
+
+ requestPtr.p->m_outstanding++;
+ ndbassert(treeNodePtr.p->m_state == TreeNode::TN_ACTIVE);
+}
+
+void
+Dbspj::scanIndex_complete(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ jam();
+ ScanIndexData& data = treeNodePtr.p->m_scanindex_data;
+ ScanFragReq*dst=(ScanFragReq*)treeNodePtr.p->m_scanindex_data.m_scanFragReq;
+ if (!data.m_fragments.isEmpty())
+ {
+ jam();
+ DihScanTabCompleteRep* rep=(DihScanTabCompleteRep*)signal->getDataPtrSend();
+ rep->tableId = dst->tableId;
+ rep->scanCookie = data.m_scanCookie;
+ sendSignal(DBDIH_REF, GSN_DIH_SCAN_TAB_COMPLETE_REP,
+ signal, DihScanTabCompleteRep::SignalLength, JBB);
+ }
+}
+
+void
+Dbspj::scanIndex_abort(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ jam();
+
+ switch(treeNodePtr.p->m_state){
+ case TreeNode::TN_BUILDING:
+ case TreeNode::TN_PREPARING:
+ case TreeNode::TN_INACTIVE:
+ case TreeNode::TN_COMPLETING:
+ case TreeNode::TN_END:
+ ndbout_c("H'%.8x H'%.8x scanIndex_abort state: %u",
+ requestPtr.p->m_transId[0],
+ requestPtr.p->m_transId[1],
+ treeNodePtr.p->m_state);
+ return;
+
+ case TreeNode::TN_ACTIVE:
+ jam();
+ break;
+ }
+
+ ScanFragNextReq* req = CAST_PTR(ScanFragNextReq, signal->getDataPtrSend());
+ req->requestInfo = ScanFragNextReq::ZCLOSE;
+ req->transId1 = requestPtr.p->m_transId[0];
+ req->transId2 = requestPtr.p->m_transId[1];
+ req->batch_size_rows = 0;
+ req->batch_size_bytes = 0;
+
+ ScanIndexData& data = treeNodePtr.p->m_scanindex_data;
+ Local_ScanFragHandle_list list(m_scanfraghandle_pool, data.m_fragments);
+ Ptr<ScanFragHandle> fragPtr;
+
+ Uint32 cnt_waiting = 0;
+ Uint32 cnt_scanning = 0;
+ for (list.first(fragPtr); !fragPtr.isNull(); list.next(fragPtr))
+ {
+ switch(fragPtr.p->m_state){
+ case ScanFragHandle::SFH_NOT_STARTED:
+ case ScanFragHandle::SFH_COMPLETE:
+ case ScanFragHandle::SFH_WAIT_CLOSE:
+ jam();
+ break;
+ case ScanFragHandle::SFH_WAIT_NEXTREQ:
+ jam();
+ cnt_waiting++; // was idle...
+ data.m_frags_outstanding++; // is closing
+ goto do_abort;
+ case ScanFragHandle::SFH_SCANNING:
+ jam();
+ cnt_scanning++;
+ goto do_abort;
+ do_abort:
+ req->senderData = fragPtr.i;
+ sendSignal(fragPtr.p->m_ref, GSN_SCAN_NEXTREQ, signal,
+ ScanFragNextReq::SignalLength, JBB);
+
+ fragPtr.p->m_state = ScanFragHandle::SFH_WAIT_CLOSE;
+ break;
+ }
+ }
+
+ ndbrequire(cnt_waiting + cnt_scanning > 0);
+ if (cnt_scanning == 0)
+ {
+ /**
+ * If all were waiting...this should increase m_outstanding
+ */
+ jam();
+ requestPtr.p->m_outstanding++;
+ }
+}
+
+Uint32
+Dbspj::scanIndex_execNODE_FAILREP(Signal* signal,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr,
+ NdbNodeBitmask nodes)
+{
+ jam();
+
+ switch(treeNodePtr.p->m_state){
+ case TreeNode::TN_PREPARING:
+ case TreeNode::TN_INACTIVE:
+ return 1;
+
+ case TreeNode::TN_BUILDING:
+ case TreeNode::TN_COMPLETING:
+ case TreeNode::TN_END:
+ return 0;
+
+ case TreeNode::TN_ACTIVE:
+ jam();
+ break;
+ }
+
+
+ Uint32 sum = 0;
+ ScanIndexData& data = treeNodePtr.p->m_scanindex_data;
+ Local_ScanFragHandle_list list(m_scanfraghandle_pool, data.m_fragments);
+ Ptr<ScanFragHandle> fragPtr;
+
+ Uint32 save0 = data.m_frags_outstanding;
+ Uint32 save1 = data.m_frags_not_complete;
+
+ for (list.first(fragPtr); !fragPtr.isNull(); list.next(fragPtr))
+ {
+ if (nodes.get(refToNode(fragPtr.p->m_ref)) == false)
+ {
+ jam();
+ /**
+ * No action needed
+ */
+ continue;
+ }
+
+ switch(fragPtr.p->m_state){
+ case ScanFragHandle::SFH_NOT_STARTED:
+ jam();
+ ndbrequire(data.m_frags_not_complete > 0);
+ data.m_frags_not_complete--;
+ // fall through
+ case ScanFragHandle::SFH_COMPLETE:
+ jam();
+ sum++; // indicate that we should abort
+ /**
+ * we could keep list of all fragments...
+ * or execute DIGETNODES again...
+ * but for now, we don't
+ */
+ break;
+ case ScanFragHandle::SFH_WAIT_CLOSE:
+ case ScanFragHandle::SFH_SCANNING:
+ jam();
+ ndbrequire(data.m_frags_outstanding > 0);
+ data.m_frags_outstanding--;
+ // fall through
+ case ScanFragHandle::SFH_WAIT_NEXTREQ:
+ jam();
+ sum++;
+ ndbrequire(data.m_frags_not_complete > 0);
+ data.m_frags_not_complete--;
+ break;
+ }
+ fragPtr.p->m_ref = 0;
+ fragPtr.p->m_state = ScanFragHandle::SFH_COMPLETE;
+ }
+
+ if (save0 != 0 && data.m_frags_outstanding == 0)
+ {
+ jam();
+ ndbrequire(requestPtr.p->m_outstanding);
+ requestPtr.p->m_outstanding--;
+ }
+
+ if (save1 != 0 && data.m_frags_not_complete == 0)
+ {
+ jam();
+ ndbrequire(requestPtr.p->m_cnt_active);
+ requestPtr.p->m_cnt_active--;
+ treeNodePtr.p->m_state = TreeNode::TN_INACTIVE;
+ mark_active(requestPtr, treeNodePtr, false);
+ }
+
+ return sum;
+}
+
+void
+Dbspj::scanIndex_cleanup(Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr)
+{
+ ScanIndexData& data = treeNodePtr.p->m_scanindex_data;
+ Local_ScanFragHandle_list list(m_scanfraghandle_pool, data.m_fragments);
+ list.remove();
+
+ if (treeNodePtr.p->m_bits & TreeNode::T_PRUNE_PATTERN)
+ {
+ jam();
+ LocalArenaPoolImpl pool(requestPtr.p->m_arena, m_dependency_map_pool);
+ Local_pattern_store pattern(pool, data.m_prunePattern);
+ pattern.release();
+ }
+ else if (treeNodePtr.p->m_bits & TreeNode::T_CONST_PRUNE)
+ {
+ jam();
+ if (data.m_constPrunePtrI != RNIL)
+ {
+ jam();
+ releaseSection(data.m_constPrunePtrI);
+ data.m_constPrunePtrI = RNIL;
+ }
+ }
+
+ cleanup_common(requestPtr, treeNodePtr);
+}
+
+/**
+ * END - MODULE SCAN INDEX
+ */
+
+/**
+ * Static OpInfo handling
+ */
+const Dbspj::OpInfo*
+Dbspj::getOpInfo(Uint32 op)
+{
+ DEBUG("getOpInfo(" << op << ")");
+ switch(op){
+ case QueryNode::QN_LOOKUP:
+ return &Dbspj::g_LookupOpInfo;
+ case QueryNode::QN_SCAN_FRAG:
+ return &Dbspj::g_ScanFragOpInfo;
+ case QueryNode::QN_SCAN_INDEX:
+ return &Dbspj::g_ScanIndexOpInfo;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * MODULE COMMON PARSE/UNPACK
+ */
+
+/**
+ * @returns dstLen + 1 on error
+ */
+static
+Uint32
+unpackList(Uint32 dstLen, Uint32 * dst, Dbspj::DABuffer & buffer)
+{
+ const Uint32 * ptr = buffer.ptr;
+ if (likely(ptr != buffer.end))
+ {
+ Uint32 tmp = * ptr++;
+ Uint32 cnt = tmp & 0xFFFF;
+
+ * dst ++ = (tmp >> 16); // Store first
+ DEBUG("cnt: " << cnt << " first: " << (tmp >> 16));
+
+ if (cnt > 1)
+ {
+ Uint32 len = cnt / 2;
+ if (unlikely(cnt >= dstLen || (ptr + len > buffer.end)))
+ goto error;
+
+ cnt --; // subtract item stored in header
+
+ for (Uint32 i = 0; i < cnt/2; i++)
+ {
+ * dst++ = (* ptr) & 0xFFFF;
+ * dst++ = (* ptr) >> 16;
+ ptr++;
+ }
+
+ if (cnt & 1)
+ {
+ * dst ++ = * ptr & 0xFFFF;
+ ptr++;
+ }
+
+ cnt ++; // readd item stored in header
+ }
+ buffer.ptr = ptr;
+ return cnt;
+ }
+ return 0;
+
+error:
+ return dstLen + 1;
+}
+
+/**
+ * This fuctions takes an array of attrinfo, and builds "header"
+ * which can be used to do random access inside the row
+ */
+Uint32
+Dbspj::buildRowHeader(RowPtr::Header * header, SegmentedSectionPtr ptr)
+{
+ Uint32 tmp, len;
+ Uint32 * dst = header->m_offset;
+ const Uint32 * const save = dst;
+ SectionReader r0(ptr, getSectionSegmentPool());
+ Uint32 offset = 0;
+ do
+ {
+ * dst++ = offset;
+ r0.getWord(&tmp);
+ len = AttributeHeader::getDataSize(tmp);
+ offset += 1 + len;
+ } while (r0.step(len));
+
+ return header->m_len = static_cast<Uint32>(dst - save);
+}
+
+/**
+ * This fuctions takes an array of attrinfo, and builds "header"
+ * which can be used to do random access inside the row
+ */
+Uint32
+Dbspj::buildRowHeader(RowPtr::Header * header, const Uint32 *& src, Uint32 len)
+{
+ Uint32 * dst = header->m_offset;
+ const Uint32 * save = dst;
+ Uint32 offset = 0;
+ for (Uint32 i = 0; i<len; i++)
+ {
+ * dst ++ = offset;
+ Uint32 tmp = * src++;
+ Uint32 tmp_len = AttributeHeader::getDataSize(tmp);
+ offset += 1 + tmp_len;
+ src += tmp_len;
+ }
+
+ return header->m_len = static_cast<Uint32>(dst - save);
+}
+
+Uint32
+Dbspj::appendToPattern(Local_pattern_store & pattern,
+ DABuffer & tree, Uint32 len)
+{
+ if (unlikely(tree.ptr + len > tree.end))
+ return DbspjErr::InvalidTreeNodeSpecification;
+
+ if (unlikely(pattern.append(tree.ptr, len)==0))
+ return DbspjErr::OutOfQueryMemory;
+
+ tree.ptr += len;
+ return 0;
+}
+
+Uint32
+Dbspj::appendParamToPattern(Local_pattern_store& dst,
+ const RowPtr::Linear & row, Uint32 col)
+{
+ /**
+ * TODO handle errors
+ */
+ Uint32 offset = row.m_header->m_offset[col];
+ const Uint32 * ptr = row.m_data + offset;
+ Uint32 len = AttributeHeader::getDataSize(* ptr ++);
+ /* Param COL's converted to DATA when appended to pattern */
+ Uint32 info = QueryPattern::data(len);
+ return dst.append(&info,1) && dst.append(ptr,len) ? 0 : DbspjErr::OutOfQueryMemory;
+}
+
+Uint32
+Dbspj::appendParamHeadToPattern(Local_pattern_store& dst,
+ const RowPtr::Linear & row, Uint32 col)
+{
+ /**
+ * TODO handle errors
+ */
+ Uint32 offset = row.m_header->m_offset[col];
+ const Uint32 * ptr = row.m_data + offset;
+ Uint32 len = AttributeHeader::getDataSize(*ptr);
+ /* Param COL's converted to DATA when appended to pattern */
+ Uint32 info = QueryPattern::data(len+1);
+ return dst.append(&info,1) && dst.append(ptr,len+1) ? 0 : DbspjErr::OutOfQueryMemory;
+}
+
+Uint32
+Dbspj::appendTreeToSection(Uint32 & ptrI, SectionReader & tree, Uint32 len)
+{
+ /**
+ * TODO handle errors
+ */
+ Uint32 SZ = 16;
+ Uint32 tmp[16];
+ while (len > SZ)
+ {
+ jam();
+ tree.getWords(tmp, SZ);
+ ndbrequire(appendToSection(ptrI, tmp, SZ));
+ len -= SZ;
+ }
+
+ tree.getWords(tmp, len);
+ return appendToSection(ptrI, tmp, len) ? 0 : /** todo error code */ 1;
+#if TODO
+err:
+ return 1;
+#endif
+}
+
+void
+Dbspj::getCorrelationData(const RowPtr::Section & row,
+ Uint32 col,
+ Uint32& correlationNumber)
+{
+ /**
+ * TODO handle errors
+ */
+ SegmentedSectionPtr ptr(row.m_dataPtr);
+ SectionReader reader(ptr, getSectionSegmentPool());
+ Uint32 offset = row.m_header->m_offset[col];
+ ndbrequire(reader.step(offset));
+ Uint32 tmp;
+ ndbrequire(reader.getWord(&tmp));
+ Uint32 len = AttributeHeader::getDataSize(tmp);
+ ndbrequire(len == 1);
+ ndbrequire(AttributeHeader::getAttributeId(tmp) == AttributeHeader::CORR_FACTOR32);
+ ndbrequire(reader.getWord(&correlationNumber));
+}
+
+void
+Dbspj::getCorrelationData(const RowPtr::Linear & row,
+ Uint32 col,
+ Uint32& correlationNumber)
+{
+ /**
+ * TODO handle errors
+ */
+ Uint32 offset = row.m_header->m_offset[col];
+ Uint32 tmp = row.m_data[offset];
+ Uint32 len = AttributeHeader::getDataSize(tmp);
+ ndbrequire(len == 1);
+ ndbrequire(AttributeHeader::getAttributeId(tmp) == AttributeHeader::CORR_FACTOR32);
+ correlationNumber = row.m_data[offset+1];
+}
+
+Uint32
+Dbspj::appendColToSection(Uint32 & dst, const RowPtr::Section & row,
+ Uint32 col, bool& hasNull)
+{
+ /**
+ * TODO handle errors
+ */
+ SegmentedSectionPtr ptr(row.m_dataPtr);
+ SectionReader reader(ptr, getSectionSegmentPool());
+ Uint32 offset = row.m_header->m_offset[col];
+ ndbrequire(reader.step(offset));
+ Uint32 tmp;
+ ndbrequire(reader.getWord(&tmp));
+ Uint32 len = AttributeHeader::getDataSize(tmp);
+ if (unlikely(len==0))
+ {
+ jam();
+ hasNull = true; // NULL-value in key
+ return 0;
+ }
+ return appendTreeToSection(dst, reader, len);
+}
+
+Uint32
+Dbspj::appendColToSection(Uint32 & dst, const RowPtr::Linear & row,
+ Uint32 col, bool& hasNull)
+{
+ /**
+ * TODO handle errors
+ */
+ Uint32 offset = row.m_header->m_offset[col];
+ const Uint32 * ptr = row.m_data + offset;
+ Uint32 len = AttributeHeader::getDataSize(* ptr ++);
+ if (unlikely(len==0))
+ {
+ jam();
+ hasNull = true; // NULL-value in key
+ return 0;
+ }
+ return appendToSection(dst, ptr, len) ? 0 : DbspjErr::InvalidPattern;
+}
+
+Uint32
+Dbspj::appendAttrinfoToSection(Uint32 & dst, const RowPtr::Linear & row,
+ Uint32 col, bool& hasNull)
+{
+ /**
+ * TODO handle errors
+ */
+ Uint32 offset = row.m_header->m_offset[col];
+ const Uint32 * ptr = row.m_data + offset;
+ Uint32 len = AttributeHeader::getDataSize(* ptr);
+ if (unlikely(len==0))
+ {
+ jam();
+ hasNull = true; // NULL-value in key
+ }
+ return appendToSection(dst, ptr, 1 + len) ? 0 : DbspjErr::InvalidPattern;
+}
+
+Uint32
+Dbspj::appendAttrinfoToSection(Uint32 & dst, const RowPtr::Section & row,
+ Uint32 col, bool& hasNull)
+{
+ /**
+ * TODO handle errors
+ */
+ SegmentedSectionPtr ptr(row.m_dataPtr);
+ SectionReader reader(ptr, getSectionSegmentPool());
+ Uint32 offset = row.m_header->m_offset[col];
+ ndbrequire(reader.step(offset));
+ Uint32 tmp;
+ ndbrequire(reader.peekWord(&tmp));
+ Uint32 len = AttributeHeader::getDataSize(tmp);
+ if (unlikely(len==0))
+ {
+ jam();
+ hasNull = true; // NULL-value in key
+ }
+ return appendTreeToSection(dst, reader, 1 + len);
+}
+
+/**
+ * 'PkCol' is the composite NDB$PK column in an unique index consisting of
+ * a fragment id and the composite PK value (all PK columns concatenated)
+ */
+Uint32
+Dbspj::appendPkColToSection(Uint32 & dst, const RowPtr::Section & row, Uint32 col)
+{
+ /**
+ * TODO handle errors
+ */
+ SegmentedSectionPtr ptr(row.m_dataPtr);
+ SectionReader reader(ptr, getSectionSegmentPool());
+ Uint32 offset = row.m_header->m_offset[col];
+ ndbrequire(reader.step(offset));
+ Uint32 tmp;
+ ndbrequire(reader.getWord(&tmp));
+ Uint32 len = AttributeHeader::getDataSize(tmp);
+ ndbrequire(len>1); // NULL-value in PkKey is an error
+ ndbrequire(reader.step(1)); // Skip fragid
+ return appendTreeToSection(dst, reader, len-1);
+}
+
+/**
+ * 'PkCol' is the composite NDB$PK column in an unique index consisting of
+ * a fragment id and the composite PK value (all PK columns concatenated)
+ */
+Uint32
+Dbspj::appendPkColToSection(Uint32 & dst, const RowPtr::Linear & row, Uint32 col)
+{
+ Uint32 offset = row.m_header->m_offset[col];
+ Uint32 tmp = row.m_data[offset];
+ Uint32 len = AttributeHeader::getDataSize(tmp);
+ ndbrequire(len>1); // NULL-value in PkKey is an error
+ return appendToSection(dst, row.m_data+offset+2, len - 1) ? 0 : /** todo error code */ 1;
+}
+
+Uint32
+Dbspj::appendFromParent(Uint32 & dst, Local_pattern_store& pattern,
+ Local_pattern_store::ConstDataBufferIterator& it,
+ Uint32 levels, const RowPtr & rowptr,
+ bool& hasNull)
+{
+ Ptr<TreeNode> treeNodePtr;
+ m_treenode_pool.getPtr(treeNodePtr, rowptr.m_src_node_ptrI);
+ Uint32 corrVal = rowptr.m_src_correlation;
+ RowPtr targetRow;
+ while (levels--)
+ {
+ jam();
+ if (unlikely(treeNodePtr.p->m_parentPtrI == RNIL))
+ {
+ DEBUG_CRASH();
+ return DbspjErr::InvalidPattern;
+ }
+ m_treenode_pool.getPtr(treeNodePtr, treeNodePtr.p->m_parentPtrI);
+ if (unlikely((treeNodePtr.p->m_bits & TreeNode::T_ROW_BUFFER_MAP) == 0))
+ {
+ DEBUG_CRASH();
+ return DbspjErr::InvalidPattern;
+ }
+
+ RowRef ref;
+ treeNodePtr.p->m_row_map.copyto(ref);
+ Uint32 allocator = ref.m_allocator;
+ const Uint32 * mapptr;
+ if (allocator == 0)
+ {
+ jam();
+ mapptr = get_row_ptr_stack(ref);
+ }
+ else
+ {
+ jam();
+ mapptr = get_row_ptr_var(ref);
+ }
+
+ Uint32 pos = corrVal >> 16; // parent corr-val
+ if (unlikely(! (pos < treeNodePtr.p->m_row_map.m_size)))
+ {
+ DEBUG_CRASH();
+ return DbspjErr::InvalidPattern;
+ }
+
+ // load ref to parent row
+ treeNodePtr.p->m_row_map.load(mapptr, pos, ref);
+
+ const Uint32 * rowptr;
+ if (allocator == 0)
+ {
+ jam();
+ rowptr = get_row_ptr_stack(ref);
+ }
+ else
+ {
+ jam();
+ rowptr = get_row_ptr_var(ref);
+ }
+ setupRowPtr(treeNodePtr, targetRow, ref, rowptr);
+
+ if (levels)
+ {
+ jam();
+ getCorrelationData(targetRow.m_row_data.m_linear,
+ targetRow.m_row_data.m_linear.m_header->m_len - 1,
+ corrVal);
+ }
+ }
+
+ if (unlikely(it.isNull()))
+ {
+ DEBUG_CRASH();
+ return DbspjErr::InvalidPattern;
+ }
+
+ Uint32 info = *it.data;
+ Uint32 type = QueryPattern::getType(info);
+ Uint32 val = QueryPattern::getLength(info);
+ pattern.next(it);
+ switch(type){
+ case QueryPattern::P_COL:
+ jam();
+ return appendColToSection(dst, targetRow.m_row_data.m_linear, val, hasNull);
+ break;
+ case QueryPattern::P_UNQ_PK:
+ jam();
+ return appendPkColToSection(dst, targetRow.m_row_data.m_linear, val);
+ break;
+ case QueryPattern::P_ATTRINFO:
+ jam();
+ return appendAttrinfoToSection(dst, targetRow.m_row_data.m_linear, val, hasNull);
+ break;
+ case QueryPattern::P_DATA:
+ jam();
+ // retreiving DATA from parent...is...an error
+ break;
+ case QueryPattern::P_PARENT:
+ jam();
+ // no point in nesting P_PARENT...an error
+ break;
+ case QueryPattern::P_PARAM:
+ case QueryPattern::P_PARAM_HEADER:
+ jam();
+ // should have been expanded during build
+ break;
+ }
+
+ DEBUG_CRASH();
+ return DbspjErr::InvalidPattern;
+}
+
+Uint32
+Dbspj::appendDataToSection(Uint32 & ptrI,
+ Local_pattern_store& pattern,
+ Local_pattern_store::ConstDataBufferIterator& it,
+ Uint32 len, bool& hasNull)
+{
+ if (unlikely(len==0))
+ {
+ jam();
+ hasNull = true;
+ return 0;
+ }
+
+#if 0
+ /**
+ * TODO handle errors
+ */
+ Uint32 tmp[NDB_SECTION_SEGMENT_SZ];
+ while (len > NDB_SECTION_SEGMENT_SZ)
+ {
+ pattern.copyout(tmp, NDB_SECTION_SEGMENT_SZ, it);
+ appendToSection(ptrI, tmp, NDB_SECTION_SEGMENT_SZ);
+ len -= NDB_SECTION_SEGMENT_SZ;
+ }
+
+ pattern.copyout(tmp, len, it);
+ appendToSection(ptrI, tmp, len);
+ return 0;
+#else
+ Uint32 remaining = len;
+ Uint32 dstIdx = 0;
+ Uint32 tmp[NDB_SECTION_SEGMENT_SZ];
+
+ while (remaining > 0 && !it.isNull())
+ {
+ tmp[dstIdx] = *it.data;
+ remaining--;
+ dstIdx++;
+ pattern.next(it);
+ if (dstIdx == NDB_SECTION_SEGMENT_SZ || remaining == 0)
+ {
+ if (!appendToSection(ptrI, tmp, dstIdx))
+ {
+ DEBUG_CRASH();
+ return DbspjErr::InvalidPattern;
+ }
+ dstIdx = 0;
+ }
+ }
+ if (remaining > 0)
+ {
+ DEBUG_CRASH();
+ return DbspjErr::InvalidPattern;
+ }
+ else
+ {
+ return 0;
+ }
+#endif
+}
+
+Uint32
+Dbspj::createEmptySection(Uint32 & dst)
+{
+ Uint32 tmp;
+ SegmentedSectionPtr ptr;
+ if (likely(import(ptr, &tmp, 0)))
+ {
+ jam();
+ dst = ptr.i;
+ return 0;
+ }
+
+ jam();
+ return DbspjErr::OutOfSectionMemory;
+}
+
+/**
+ * This function takes a pattern and a row and expands it into a section
+ */
+Uint32
+Dbspj::expandS(Uint32 & _dst, Local_pattern_store& pattern,
+ const RowPtr & row, bool& hasNull)
+{
+ Uint32 err;
+ Uint32 dst = _dst;
+ hasNull = false;
+ Local_pattern_store::ConstDataBufferIterator it;
+ pattern.first(it);
+ while (!it.isNull())
+ {
+ Uint32 info = *it.data;
+ Uint32 type = QueryPattern::getType(info);
+ Uint32 val = QueryPattern::getLength(info);
+ pattern.next(it);
+ switch(type){
+ case QueryPattern::P_COL:
+ jam();
+ err = appendColToSection(dst, row.m_row_data.m_section, val, hasNull);
+ break;
+ case QueryPattern::P_UNQ_PK:
+ jam();
+ err = appendPkColToSection(dst, row.m_row_data.m_section, val);
+ break;
+ case QueryPattern::P_ATTRINFO:
+ jam();
+ err = appendAttrinfoToSection(dst, row.m_row_data.m_section, val, hasNull);
+ break;
+ case QueryPattern::P_DATA:
+ jam();
+ err = appendDataToSection(dst, pattern, it, val, hasNull);
+ break;
+ case QueryPattern::P_PARENT:
+ jam();
+ // P_PARENT is a prefix to another pattern token
+ // that permits code to access rows from earlier than imediate parent.
+ // val is no of levels to move up the tree
+ err = appendFromParent(dst, pattern, it, val, row, hasNull);
+ break;
+ // PARAM's was converted to DATA by ::expand(pattern...)
+ case QueryPattern::P_PARAM:
+ case QueryPattern::P_PARAM_HEADER:
+ default:
+ jam();
+ err = DbspjErr::InvalidPattern;
+ DEBUG_CRASH();
+ }
+ if (unlikely(err != 0))
+ {
+ jam();
+ DEBUG_CRASH();
+ goto error;
+ }
+ }
+
+ _dst = dst;
+ return 0;
+error:
+ jam();
+ return err;
+}
+
+/**
+ * This function takes a pattern and a row and expands it into a section
+ */
+Uint32
+Dbspj::expandL(Uint32 & _dst, Local_pattern_store& pattern,
+ const RowPtr & row, bool& hasNull)
+{
+ Uint32 err;
+ Uint32 dst = _dst;
+ hasNull = false;
+ Local_pattern_store::ConstDataBufferIterator it;
+ pattern.first(it);
+ while (!it.isNull())
+ {
+ Uint32 info = *it.data;
+ Uint32 type = QueryPattern::getType(info);
+ Uint32 val = QueryPattern::getLength(info);
+ pattern.next(it);
+ switch(type){
+ case QueryPattern::P_COL:
+ jam();
+ err = appendColToSection(dst, row.m_row_data.m_linear, val, hasNull);
+ break;
+ case QueryPattern::P_UNQ_PK:
+ jam();
+ err = appendPkColToSection(dst, row.m_row_data.m_linear, val);
+ break;
+ case QueryPattern::P_ATTRINFO:
+ jam();
+ err = appendAttrinfoToSection(dst, row.m_row_data.m_linear, val, hasNull);
+ break;
+ case QueryPattern::P_DATA:
+ jam();
+ err = appendDataToSection(dst, pattern, it, val, hasNull);
+ break;
+ case QueryPattern::P_PARENT:
+ jam();
+ // P_PARENT is a prefix to another pattern token
+ // that permits code to access rows from earlier than imediate parent
+ // val is no of levels to move up the tree
+ err = appendFromParent(dst, pattern, it, val, row, hasNull);
+ break;
+ // PARAM's was converted to DATA by ::expand(pattern...)
+ case QueryPattern::P_PARAM:
+ case QueryPattern::P_PARAM_HEADER:
+ default:
+ jam();
+ err = DbspjErr::InvalidPattern;
+ DEBUG_CRASH();
+ }
+ if (unlikely(err != 0))
+ {
+ jam();
+ DEBUG_CRASH();
+ goto error;
+ }
+ }
+
+ _dst = dst;
+ return 0;
+error:
+ jam();
+ return err;
+}
+
+Uint32
+Dbspj::expand(Uint32 & ptrI, DABuffer& pattern, Uint32 len,
+ DABuffer& param, Uint32 paramCnt, bool& hasNull)
+{
+ /**
+ * TODO handle error
+ */
+ Uint32 err;
+ Uint32 tmp[1+MAX_ATTRIBUTES_IN_TABLE];
+ struct RowPtr::Linear row;
+ row.m_data = param.ptr;
+ row.m_header = CAST_PTR(RowPtr::Header, &tmp[0]);
+ buildRowHeader(CAST_PTR(RowPtr::Header, &tmp[0]), param.ptr, paramCnt);
+
+ Uint32 dst = ptrI;
+ const Uint32 * ptr = pattern.ptr;
+ const Uint32 * end = ptr + len;
+ hasNull = false;
+
+ for (; ptr < end; )
+ {
+ Uint32 info = * ptr++;
+ Uint32 type = QueryPattern::getType(info);
+ Uint32 val = QueryPattern::getLength(info);
+ switch(type){
+ case QueryPattern::P_PARAM:
+ jam();
+ ndbassert(val < paramCnt);
+ err = appendColToSection(dst, row, val, hasNull);
+ break;
+ case QueryPattern::P_PARAM_HEADER:
+ jam();
+ ndbassert(val < paramCnt);
+ err = appendAttrinfoToSection(dst, row, val, hasNull);
+ break;
+ case QueryPattern::P_DATA:
+ if (unlikely(val==0))
+ {
+ jam();
+ hasNull = true;
+ err = 0;
+ }
+ else if (likely(appendToSection(dst, ptr, val)))
+ {
+ jam();
+ err = 0;
+ }
+ else
+ {
+ jam();
+ err = DbspjErr::InvalidPattern;
+ }
+ ptr += val;
+ break;
+ case QueryPattern::P_COL: // (linked) COL's not expected here
+ case QueryPattern::P_PARENT: // Prefix to P_COL
+ case QueryPattern::P_ATTRINFO:
+ case QueryPattern::P_UNQ_PK:
+ default:
+ jam();
+ jamLine(type);
+ err = DbspjErr::InvalidPattern;
+ DEBUG_CRASH();
+ }
+ if (unlikely(err != 0))
+ {
+ jam();
+ DEBUG_CRASH();
+ goto error;
+ }
+ }
+
+ /**
+ * Iterate forward
+ */
+ pattern.ptr = end;
+
+error:
+ jam();
+ ptrI = dst;
+ return err;
+}
+
+Uint32
+Dbspj::expand(Local_pattern_store& dst, Ptr<TreeNode> treeNodePtr,
+ DABuffer& pattern, Uint32 len,
+ DABuffer& param, Uint32 paramCnt)
+{
+ /**
+ * TODO handle error
+ */
+ Uint32 err;
+ Uint32 tmp[1+MAX_ATTRIBUTES_IN_TABLE];
+ struct RowPtr::Linear row;
+ row.m_header = CAST_PTR(RowPtr::Header, &tmp[0]);
+ row.m_data = param.ptr;
+ buildRowHeader(CAST_PTR(RowPtr::Header, &tmp[0]), param.ptr, paramCnt);
+
+ const Uint32 * end = pattern.ptr + len;
+ for (; pattern.ptr < end; )
+ {
+ Uint32 info = *pattern.ptr;
+ Uint32 type = QueryPattern::getType(info);
+ Uint32 val = QueryPattern::getLength(info);
+ switch(type){
+ case QueryPattern::P_COL:
+ case QueryPattern::P_UNQ_PK:
+ case QueryPattern::P_ATTRINFO:
+ jam();
+ err = appendToPattern(dst, pattern, 1);
+ break;
+ case QueryPattern::P_DATA:
+ jam();
+ err = appendToPattern(dst, pattern, val+1);
+ break;
+ case QueryPattern::P_PARAM:
+ jam();
+ // NOTE: Converted to P_DATA by appendParamToPattern
+ ndbassert(val < paramCnt);
+ err = appendParamToPattern(dst, row, val);
+ pattern.ptr++;
+ break;
+ case QueryPattern::P_PARAM_HEADER:
+ jam();
+ // NOTE: Converted to P_DATA by appendParamHeadToPattern
+ ndbassert(val < paramCnt);
+ err = appendParamHeadToPattern(dst, row, val);
+ pattern.ptr++;
+ break;
+ case QueryPattern::P_PARENT: // Prefix to P_COL
+ {
+ jam();
+ err = appendToPattern(dst, pattern, 1);
+
+ // Locate requested grandparent and request it to
+ // T_ROW_BUFFER its result rows
+ Ptr<TreeNode> parentPtr;
+ m_treenode_pool.getPtr(parentPtr, treeNodePtr.p->m_parentPtrI);
+ while (val--)
+ {
+ jam();
+ ndbassert(parentPtr.p->m_parentPtrI != RNIL);
+ m_treenode_pool.getPtr(parentPtr, parentPtr.p->m_parentPtrI);
+ parentPtr.p->m_bits |= TreeNode::T_ROW_BUFFER;
+ parentPtr.p->m_bits |= TreeNode::T_ROW_BUFFER_MAP;
+ }
+ Ptr<Request> requestPtr;
+ m_request_pool.getPtr(requestPtr, treeNodePtr.p->m_requestPtrI);
+ requestPtr.p->m_bits |= Request::RT_ROW_BUFFERS;
+ break;
+ }
+ default:
+ jam();
+ err = DbspjErr::InvalidPattern;
+ DEBUG_CRASH();
+ }
+
+ if (unlikely(err != 0))
+ {
+ DEBUG_CRASH();
+ goto error;
+ }
+ }
+ return 0;
+
+error:
+ jam();
+ return err;
+}
+
+Uint32
+Dbspj::parseDA(Build_context& ctx,
+ Ptr<Request> requestPtr,
+ Ptr<TreeNode> treeNodePtr,
+ DABuffer& tree, Uint32 treeBits,
+ DABuffer& param, Uint32 paramBits)
+{
+ Uint32 err;
+ Uint32 attrInfoPtrI = RNIL;
+ Uint32 attrParamPtrI = RNIL;
+
+ do
+ {
+ if (treeBits & DABits::NI_HAS_PARENT)
+ {
+ jam();
+ DEBUG("NI_HAS_PARENT");
+ /**
+ * OPTIONAL PART 1:
+ *
+ * Parent nodes are stored first in optional part
+ * this is a list of 16-bit numbers refering to
+ * *earlier* nodes in tree
+ * the list stores length of list as first 16-bit
+ */
+ err = DbspjErr::InvalidTreeNodeSpecification;
+ Uint32 dst[63];
+ Uint32 cnt = unpackList(NDB_ARRAY_SIZE(dst), dst, tree);
+ if (unlikely(cnt > NDB_ARRAY_SIZE(dst)))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+
+ err = 0;
+
+ if (unlikely(cnt!=1))
+ {
+ /**
+ * Only a single parent supported for now, i.e only trees
+ */
+ DEBUG_CRASH();
+ }
+
+ for (Uint32 i = 0; i<cnt; i++)
+ {
+ DEBUG("adding " << dst[i] << " as parent");
+ Ptr<TreeNode> parentPtr = ctx.m_node_list[dst[i]];
+ LocalArenaPoolImpl pool(requestPtr.p->m_arena, m_dependency_map_pool);
+ Local_dependency_map map(pool, parentPtr.p->m_dependent_nodes);
+ if (unlikely(!map.append(&treeNodePtr.i, 1)))
+ {
+ err = DbspjErr::OutOfQueryMemory;
+ DEBUG_CRASH();
+ break;
+ }
+ parentPtr.p->m_bits &= ~(Uint32)TreeNode::T_LEAF;
+ treeNodePtr.p->m_parentPtrI = parentPtr.i;
+ }
+
+ if (unlikely(err != 0))
+ break;
+ } // DABits::NI_HAS_PARENT
+
+ err = DbspjErr::InvalidTreeParametersSpecificationKeyParamBitsMissmatch;
+ if (unlikely( ((treeBits & DABits::NI_KEY_PARAMS)==0) !=
+ ((paramBits & DABits::PI_KEY_PARAMS)==0)))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+
+ if (treeBits & (DABits::NI_KEY_PARAMS
+ | DABits::NI_KEY_LINKED
+ | DABits::NI_KEY_CONSTS))
+ {
+ jam();
+ DEBUG("NI_KEY_PARAMS | NI_KEY_LINKED | NI_KEY_CONSTS");
+
+ /**
+ * OPTIONAL PART 2:
+ *
+ * If keys are parametrized or linked
+ * DATA0[LO/HI] - Length of key pattern/#parameters to key
+ */
+ Uint32 len_cnt = * tree.ptr ++;
+ Uint32 len = len_cnt & 0xFFFF; // length of pattern in words
+ Uint32 cnt = len_cnt >> 16; // no of parameters
+
+ LocalArenaPoolImpl pool(requestPtr.p->m_arena, m_dependency_map_pool);
+ Local_pattern_store pattern(pool, treeNodePtr.p->m_keyPattern);
+
+ err = DbspjErr::InvalidTreeParametersSpecificationIncorrectKeyParamCount;
+ if (unlikely( ((cnt==0) != ((treeBits & DABits::NI_KEY_PARAMS) == 0)) ||
+ ((cnt==0) != ((paramBits & DABits::PI_KEY_PARAMS) == 0))))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+
+ if (treeBits & DABits::NI_KEY_LINKED)
+ {
+ jam();
+ DEBUG("LINKED-KEY PATTERN w/ " << cnt << " PARAM values");
+ /**
+ * Expand pattern into a new pattern (with linked values)
+ */
+ err = expand(pattern, treeNodePtr, tree, len, param, cnt);
+
+ /**
+ * This node constructs a new key for each send
+ */
+ treeNodePtr.p->m_bits |= TreeNode::T_KEYINFO_CONSTRUCTED;
+ }
+ else
+ {
+ jam();
+ DEBUG("FIXED-KEY w/ " << cnt << " PARAM values");
+ /**
+ * Expand pattern directly into keyinfo
+ * This means a "fixed" key from here on
+ */
+ bool hasNull;
+ Uint32 keyInfoPtrI = RNIL;
+ err = expand(keyInfoPtrI, tree, len, param, cnt, hasNull);
+ if (unlikely(hasNull))
+ {
+ /* API should have elliminated requests w/ const-NULL keys */
+ jam();
+ DEBUG("BEWARE: FIXED-key contain NULL values");
+// treeNodePtr.p->m_bits |= TreeNode::T_NULL_PRUNE;
+// break;
+ ndbrequire(false);
+ }
+ treeNodePtr.p->m_send.m_keyInfoPtrI = keyInfoPtrI;
+ }
+
+ if (unlikely(err != 0))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+ } // DABits::NI_KEY_...
+
+ const Uint32 mask =
+ DABits::NI_LINKED_ATTR | DABits::NI_ATTR_INTERPRET |
+ DABits::NI_ATTR_LINKED | DABits::NI_ATTR_PARAMS;
+
+ if (((treeBits & mask) | (paramBits & DABits::PI_ATTR_LIST)) != 0)
+ {
+ jam();
+ /**
+ * OPTIONAL PART 3: attrinfo handling
+ * - NI_LINKED_ATTR - these are attributes to be passed to children
+ * - PI_ATTR_LIST - this is "user-columns" (passed as parameters)
+
+ * - NI_ATTR_INTERPRET - tree contains interpreted program
+ * - NI_ATTR_LINKED - means that the attr-info contains linked-values
+ * - NI_ATTR_PARAMS - means that the attr-info is parameterized
+ * PI_ATTR_PARAMS - means that the parameters contains attr parameters
+ *
+ * IF NI_ATTR_INTERPRET
+ * DATA0[LO/HI] = Length of program / total #arguments to program
+ * DATA1..N = Program
+ *
+ * IF NI_ATTR_PARAMS
+ * DATA0[LO/HI] = Length / #param
+ * DATA1..N = PARAM-0...PARAM-M
+ *
+ * IF PI_ATTR_INTERPRET
+ * DATA0[LO/HI] = Length of program / Length of subroutine-part
+ * DATA1..N = Program (scan filter)
+ *
+ * IF NI_ATTR_LINKED
+ * DATA0[LO/HI] = Length / #
+ *
+ *
+ */
+ Uint32 sections[5] = { 0, 0, 0, 0, 0 };
+ Uint32 * sectionptrs = 0;
+
+ bool interpreted =
+ (treeBits & DABits::NI_ATTR_INTERPRET) ||
+ (paramBits & DABits::PI_ATTR_INTERPRET) ||
+ (treeNodePtr.p->m_bits & TreeNode::T_ATTR_INTERPRETED);
+
+ if (interpreted)
+ {
+ /**
+ * Add section headers for interpreted execution
+ * and create pointer so that they can be updated later
+ */
+ jam();
+ err = DbspjErr::OutOfSectionMemory;
+ if (unlikely(!appendToSection(attrInfoPtrI, sections, 5)))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+
+ SegmentedSectionPtr ptr;
+ getSection(ptr, attrInfoPtrI);
+ sectionptrs = ptr.p->theData;
+
+ if (treeBits & DABits::NI_ATTR_INTERPRET)
+ {
+ jam();
+
+ /**
+ * Having two interpreter programs is an error.
+ */
+ err = DbspjErr::BothTreeAndParametersContainInterpretedProgram;
+ if (unlikely(paramBits & DABits::PI_ATTR_INTERPRET))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+
+ treeNodePtr.p->m_bits |= TreeNode::T_ATTR_INTERPRETED;
+ Uint32 len2 = * tree.ptr++;
+ Uint32 len_prg = len2 & 0xFFFF; // Length of interpret program
+ Uint32 len_pattern = len2 >> 16;// Length of attr param pattern
+ err = DbspjErr::OutOfSectionMemory;
+ if (unlikely(!appendToSection(attrInfoPtrI, tree.ptr, len_prg)))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+
+ tree.ptr += len_prg;
+ sectionptrs[1] = len_prg; // size of interpret program
+
+ Uint32 tmp = * tree.ptr ++; // attr-pattern header
+ Uint32 cnt = tmp & 0xFFFF;
+
+ if (treeBits & DABits::NI_ATTR_LINKED)
+ {
+ jam();
+ /**
+ * Expand pattern into a new pattern (with linked values)
+ */
+ LocalArenaPoolImpl pool(requestPtr.p->m_arena,
+ m_dependency_map_pool);
+ Local_pattern_store pattern(pool,treeNodePtr.p->m_attrParamPattern);
+ err = expand(pattern, treeNodePtr, tree, len_pattern, param, cnt);
+ if (unlikely(err))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+ /**
+ * This node constructs a new attr-info for each send
+ */
+ treeNodePtr.p->m_bits |= TreeNode::T_ATTRINFO_CONSTRUCTED;
+ }
+ else
+ {
+ jam();
+ /**
+ * Expand pattern directly into attr-info param
+ * This means a "fixed" attr-info param from here on
+ */
+ bool hasNull;
+ err = expand(attrParamPtrI, tree, len_pattern, param, cnt, hasNull);
+ if (unlikely(err))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+// ndbrequire(!hasNull);
+ }
+ }
+ else // if (treeBits & DABits::NI_ATTR_INTERPRET)
+ {
+ jam();
+ /**
+ * Only relevant for interpreted stuff
+ */
+ ndbrequire((treeBits & DABits::NI_ATTR_PARAMS) == 0);
+ ndbrequire((paramBits & DABits::PI_ATTR_PARAMS) == 0);
+ ndbrequire((treeBits & DABits::NI_ATTR_LINKED) == 0);
+
+ treeNodePtr.p->m_bits |= TreeNode::T_ATTR_INTERPRETED;
+
+ if (! (paramBits & DABits::PI_ATTR_INTERPRET))
+ {
+ jam();
+
+ /**
+ * Tree node has interpreted execution,
+ * but no interpreted program specified
+ * auto-add Exit_ok (i.e return each row)
+ */
+ Uint32 tmp = Interpreter::ExitOK();
+ err = DbspjErr::OutOfSectionMemory;
+ if (unlikely(!appendToSection(attrInfoPtrI, &tmp, 1)))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+ sectionptrs[1] = 1;
+ }
+ } // if (treeBits & DABits::NI_ATTR_INTERPRET)
+ } // if (interpreted)
+
+ if (paramBits & DABits::PI_ATTR_INTERPRET)
+ {
+ jam();
+
+ /**
+ * Add the interpreted code that represents the scan filter.
+ */
+ const Uint32 len2 = * param.ptr++;
+ Uint32 program_len = len2 & 0xFFFF;
+ Uint32 subroutine_len = len2 >> 16;
+ err = DbspjErr::OutOfSectionMemory;
+ if (unlikely(!appendToSection(attrInfoPtrI, param.ptr, program_len)))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+ /**
+ * The interpreted code is added is in the "Interpreted execute region"
+ * of the attrinfo (see Dbtup::interpreterStartLab() for details).
+ * It will thus execute before reading the attributes that constitutes
+ * the projections.
+ */
+ sectionptrs[1] = program_len;
+ param.ptr += program_len;
+
+ if (subroutine_len)
+ {
+ if (unlikely(!appendToSection(attrParamPtrI,
+ param.ptr, subroutine_len)))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+ sectionptrs[4] = subroutine_len;
+ param.ptr += subroutine_len;
+ }
+ treeNodePtr.p->m_bits |= TreeNode::T_ATTR_INTERPRETED;
+ }
+
+ Uint32 sum_read = 0;
+ Uint32 dst[MAX_ATTRIBUTES_IN_TABLE + 2];
+
+ if (paramBits & DABits::PI_ATTR_LIST)
+ {
+ jam();
+ Uint32 len = * param.ptr++;
+ DEBUG("PI_ATTR_LIST");
+
+ treeNodePtr.p->m_bits |= TreeNode::T_USER_PROJECTION;
+ err = DbspjErr::OutOfSectionMemory;
+ if (!appendToSection(attrInfoPtrI, param.ptr, len))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+
+ param.ptr += len;
+
+ /**
+ * Insert a flush of this partial result set
+ */
+ Uint32 flush[4];
+ flush[0] = AttributeHeader::FLUSH_AI << 16;
+ flush[1] = ctx.m_resultRef;
+ flush[2] = ctx.m_resultData;
+ flush[3] = ctx.m_senderRef; // RouteRef
+ if (!appendToSection(attrInfoPtrI, flush, 4))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+
+ sum_read += len + 4;
+ }
+
+ if (treeBits & DABits::NI_LINKED_ATTR)
+ {
+ jam();
+ DEBUG("NI_LINKED_ATTR");
+ err = DbspjErr::InvalidTreeNodeSpecification;
+ Uint32 cnt = unpackList(MAX_ATTRIBUTES_IN_TABLE, dst, tree);
+ if (unlikely(cnt > MAX_ATTRIBUTES_IN_TABLE))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+
+ /**
+ * AttributeHeader contains attrId in 16-higher bits
+ */
+ for (Uint32 i = 0; i<cnt; i++)
+ dst[i] <<= 16;
+
+ /**
+ * Read correlation factor
+ */
+ dst[cnt++] = AttributeHeader::CORR_FACTOR32 << 16;
+
+ err = DbspjErr::OutOfSectionMemory;
+ if (!appendToSection(attrInfoPtrI, dst, cnt))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+
+ sum_read += cnt;
+ }
+
+ if (interpreted)
+ {
+ jam();
+ /**
+ * Let reads be performed *after* interpreted program
+ * i.e in "final read"-section
+ */
+ sectionptrs[3] = sum_read;
+
+ if (attrParamPtrI != RNIL)
+ {
+ jam();
+ ndbrequire(!(treeNodePtr.p->m_bits&TreeNode::T_ATTRINFO_CONSTRUCTED));
+
+ SegmentedSectionPtr ptr;
+ getSection(ptr, attrParamPtrI);
+ {
+ SectionReader r0(ptr, getSectionSegmentPool());
+ err = appendTreeToSection(attrInfoPtrI, r0, ptr.sz);
+ sectionptrs[4] = ptr.sz;
+ if (unlikely(err != 0))
+ {
+ DEBUG_CRASH();
+ break;
+ }
+ }
+ releaseSection(attrParamPtrI);
+ }
+ }
+
+ treeNodePtr.p->m_send.m_attrInfoPtrI = attrInfoPtrI;
+ } // if (((treeBits & mask) | (paramBits & DABits::PI_ATTR_LIST)) != 0)
+
+ return 0;
+ } while (0);
+
+ return err;
+}
+
+/**
+ * END - MODULE COMMON PARSE/UNPACK
+ */
+
+/**
+ * Process a scan request for an ndb$info table. (These are used for monitoring
+ * purposes and do not contain application data.)
+ */
+void Dbspj::execDBINFO_SCANREQ(Signal *signal)
+{
+ DbinfoScanReq req= * CAST_PTR(DbinfoScanReq, &signal->theData[0]);
+ const Ndbinfo::ScanCursor* cursor =
+ CAST_CONSTPTR(Ndbinfo::ScanCursor, DbinfoScan::getCursorPtr(&req));
+ Ndbinfo::Ratelimit rl;
+
+ jamEntry();
+
+ switch(req.tableId){
+
+ // The SPJ block only implements the ndbinfo.counters table.
+ case Ndbinfo::COUNTERS_TABLEID:
+ {
+ Ndbinfo::counter_entry counters[] = {
+ { Ndbinfo::SPJ_READS_RECEIVED_COUNTER,
+ c_Counters.get_counter(CI_READS_RECEIVED) },
+ { Ndbinfo::SPJ_LOCAL_READS_SENT_COUNTER,
+ c_Counters.get_counter(CI_LOCAL_READS_SENT) },
+ { Ndbinfo::SPJ_REMOTE_READS_SENT_COUNTER,
+ c_Counters.get_counter(CI_REMOTE_READS_SENT) },
+ { Ndbinfo::SPJ_READS_NOT_FOUND_COUNTER,
+ c_Counters.get_counter(CI_READS_NOT_FOUND) },
+ { Ndbinfo::SPJ_TABLE_SCANS_RECEIVED_COUNTER,
+ c_Counters.get_counter(CI_TABLE_SCANS_RECEIVED) },
+ { Ndbinfo::SPJ_LOCAL_TABLE_SCANS_SENT_COUNTER,
+ c_Counters.get_counter(CI_LOCAL_TABLE_SCANS_SENT) },
+ { Ndbinfo::SPJ_RANGE_SCANS_RECEIVED_COUNTER,
+ c_Counters.get_counter(CI_RANGE_SCANS_RECEIVED) },
+ { Ndbinfo::SPJ_LOCAL_RANGE_SCANS_SENT_COUNTER,
+ c_Counters.get_counter(CI_LOCAL_RANGE_SCANS_SENT) },
+ { Ndbinfo::SPJ_REMOTE_RANGE_SCANS_SENT_COUNTER,
+ c_Counters.get_counter(CI_REMOTE_RANGE_SCANS_SENT) },
+ { Ndbinfo::SPJ_SCAN_BATCHES_RETURNED_COUNTER,
+ c_Counters.get_counter(CI_SCAN_BATCHES_RETURNED) },
+ { Ndbinfo::SPJ_SCAN_ROWS_RETURNED_COUNTER,
+ c_Counters.get_counter(CI_SCAN_ROWS_RETURNED) },
+ { Ndbinfo::SPJ_PRUNED_RANGE_SCANS_RECEIVED_COUNTER,
+ c_Counters.get_counter(CI_PRUNED_RANGE_SCANS_RECEIVED) },
+ { Ndbinfo::SPJ_CONST_PRUNED_RANGE_SCANS_RECEIVED_COUNTER,
+ c_Counters.get_counter(CI_CONST_PRUNED_RANGE_SCANS_RECEIVED) }
+ };
+ const size_t num_counters = sizeof(counters) / sizeof(counters[0]);
+
+ Uint32 i = cursor->data[0];
+ const BlockNumber bn = blockToMain(number());
+ while(i < num_counters)
+ {
+ jam();
+ Ndbinfo::Row row(signal, req);
+ row.write_uint32(getOwnNodeId());
+ row.write_uint32(bn); // block number
+ row.write_uint32(instance()); // block instance
+ row.write_uint32(counters[i].id);
+
+ row.write_uint64(counters[i].val);
+ ndbinfo_send_row(signal, req, row, rl);
+ i++;
+ if (rl.need_break(req))
+ {
+ jam();
+ ndbinfo_send_scan_break(signal, req, rl, i);
+ return;
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ ndbinfo_send_scan_conf(signal, req, rl);
+} // Dbspj::execDBINFO_SCANREQ(Signal *signal)
=== added file 'storage/ndb/src/kernel/blocks/dbspj/DbspjProxy.cpp'
--- a/storage/ndb/src/kernel/blocks/dbspj/DbspjProxy.cpp 1970-01-01 00:00:00 +0000
+++ b/storage/ndb/src/kernel/blocks/dbspj/DbspjProxy.cpp 2011-02-23 19:28:26 +0000
@@ -0,0 +1,38 @@
+/*
+ Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "DbspjProxy.hpp"
+#include "Dbspj.hpp"
+
+DbspjProxy::DbspjProxy(Block_context& ctx) :
+ LocalProxy(DBSPJ, ctx)
+{
+}
+
+DbspjProxy::~DbspjProxy()
+{
+}
+
+SimulatedBlock*
+DbspjProxy::newWorker(Uint32 instanceNo)
+{
+ ndbout << "Instantiating Dbspj worker no " << instanceNo << endl;
+ return new Dbspj(m_ctx, instanceNo);
+}
+
+
+BLOCK_FUNCTIONS(DbspjProxy)
=== added file 'storage/ndb/src/kernel/blocks/dbspj/DbspjProxy.hpp'
--- a/storage/ndb/src/kernel/blocks/dbspj/DbspjProxy.hpp 1970-01-01 00:00:00 +0000
+++ b/storage/ndb/src/kernel/blocks/dbspj/DbspjProxy.hpp 2011-02-23 19:28:26 +0000
@@ -0,0 +1,32 @@
+/* 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 NDB_DBSPJ_PROXY_HPP
+#define NDB_DBSPJ_PROXY_HPP
+
+#include <LocalProxy.hpp>
+
+class DbspjProxy : public LocalProxy {
+public:
+ DbspjProxy(Block_context& ctx);
+ virtual ~DbspjProxy();
+ BLOCK_DEFINES(DbspjProxy);
+
+protected:
+ virtual SimulatedBlock* newWorker(Uint32 instanceNo);
+
+};
+
+#endif
=== modified file 'storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp'
--- a/storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp 2011-02-03 14:20:36 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp 2011-02-23 19:28:26 +0000
@@ -900,6 +900,7 @@ public:
/* Distribution information */
// TODO : Consider placing this info into other records
Uint8 distributionKeyIndicator;
+ Uint8 viaSPJFlag; /* Send request via the SPJ block.*/
UintR distributionKey;
/* End of fields used by TCKEYREQ/TCINDXREQ/SCANTABREQ */
@@ -1236,12 +1237,22 @@ public:
Uint16 batch_size_rows;
};
Uint32 batch_byte_size;
+ Uint32 m_scan_block_no;
Uint32 scanRequestInfo; // ScanFrag format
// Close is ordered
bool m_close_scan_req;
- };
+ // All SCAN_FRAGCONS should be passed on to the API as SCAN_TABCONFS.
+ // This is needed to correctly propagate 'node masks' when scanning via the
+ // SPJ block.
+ bool m_pass_all_confs;
+
+ /**
+ * Send opcount/total len as different words
+ */
+ bool m_4word_conf;
+ };
typedef Ptr<ScanRecord> ScanRecordPtr;
/*************************************************************************>*/
=== modified file 'storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp'
--- a/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp 2011-02-15 10:52:32 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp 2011-02-23 19:28:26 +0000
@@ -2790,7 +2790,8 @@ void Dbtc::execTCKEYREQ(Signal* signal)
Uint8 TNoDiskFlag = TcKeyReq::getNoDiskFlag(Treqinfo);
Uint8 TexecuteFlag = TexecFlag;
Uint8 Treorg = TcKeyReq::getReorgFlag(Treqinfo);
- Uint8 Tqueue = TcKeyReq::getQueueOnRedoProblemFlag(Treqinfo);
+ const Uint8 TViaSPJFlag = TcKeyReq::getViaSPJFlag(Treqinfo);
+ const Uint8 Tqueue = TcKeyReq::getQueueOnRedoProblemFlag(Treqinfo);
if (Treorg)
{
@@ -2809,6 +2810,7 @@ void Dbtc::execTCKEYREQ(Signal* signal)
regCachePtr->opExec = TInterpretedFlag;
regCachePtr->distributionKeyIndicator = TDistrKeyFlag;
regCachePtr->m_no_disk_flag = TNoDiskFlag;
+ regCachePtr->viaSPJFlag = TViaSPJFlag;
regCachePtr->m_op_queue = Tqueue;
//-------------------------------------------------------------
@@ -3369,7 +3371,14 @@ void Dbtc::attrinfoDihReceivedLab(Signal
jam();
arrGuard(Tnode, MAX_NDB_NODES);
Uint32 instanceKey = regTcPtr->lqhInstanceKey;
- BlockReference lqhRef = numberToRef(DBLQH, instanceKey, Tnode);
+ BlockReference lqhRef;
+ if(regCachePtr->viaSPJFlag){
+ //ndbout << "TC:Choosing SPJ." << endl;
+ lqhRef = numberToRef(DBSPJ, Tnode); // Only 1 instance
+ }else{
+ //ndbout << "TC:Choosing LQH." << endl;
+ lqhRef = numberToRef(DBLQH, instanceKey, Tnode);
+ }
packLqhkeyreq(signal, lqhRef);
}
else
@@ -4094,6 +4103,12 @@ void Dbtc::execSIGNAL_DROPPED_REP(Signal
void Dbtc::execLQHKEYCONF(Signal* signal)
{
const LqhKeyConf * const lqhKeyConf = (LqhKeyConf *)signal->getDataPtr();
+#ifdef UNUSED
+ ndbout << "TC: Received LQHKEYCONF"
+ << " transId1=" << lqhKeyConf-> transId1
+ << " transId2=" << lqhKeyConf-> transId2
+ << endl;
+#endif /*UNUSED*/
UintR compare_transid1, compare_transid2;
BlockReference tlastLqhBlockref;
UintR tlastLqhConnect;
@@ -7695,7 +7710,21 @@ void Dbtc::timeOutFoundFragLab(Signal* s
{
ScanFragRecPtr ptr;
c_scan_frag_pool.getPtr(ptr, TscanConPtr);
- DEBUG(TscanConPtr << " timeOutFoundFragLab: scanFragState = "<< ptr.p->scanFragState);
+#ifdef VM_TRACE
+ {
+ ScanRecordPtr scanptr;
+ scanptr.i = ptr.p->scanRec;
+ ptrCheckGuard(scanptr, cscanrecFileSize, scanRecord);
+ ApiConnectRecordPtr TlocalApiConnectptr;
+ TlocalApiConnectptr.i = scanptr.p->scanApiRec;
+ ptrCheckGuard(TlocalApiConnectptr, capiConnectFilesize, apiConnectRecord);
+
+ DEBUG("[ H'" << hex << TlocalApiConnectptr.p->transid[0]
+ << " H'" << TlocalApiConnectptr.p->transid[1] << "] "
+ << TscanConPtr << " timeOutFoundFragLab: scanFragState = "
+ << ptr.p->scanFragState);
+ }
+#endif
const Uint32 time_out_param= ctimeOutValue;
const Uint32 old_time_out_param= c_abortRec.oldTimeOutValue;
@@ -10223,6 +10252,7 @@ Dbtc::initScanrec(ScanRecordPtr scanptr,
scanptr.p->first_batch_size_rows = scanTabReq->first_batch_size;
scanptr.p->batch_byte_size = scanTabReq->batch_byte_size;
scanptr.p->batch_size_rows = noOprecPerFrag;
+ scanptr.p->m_scan_block_no = DBLQH;
Uint32 tmp = 0;
ScanFragReq::setLockMode(tmp, ScanTabReq::getLockMode(ri));
@@ -10233,13 +10263,20 @@ Dbtc::initScanrec(ScanRecordPtr scanptr,
ScanFragReq::setDescendingFlag(tmp, ScanTabReq::getDescendingFlag(ri));
ScanFragReq::setTupScanFlag(tmp, ScanTabReq::getTupScanFlag(ri));
ScanFragReq::setNoDiskFlag(tmp, ScanTabReq::getNoDiskFlag(ri));
-
+ if (ScanTabReq::getViaSPJFlag(ri))
+ {
+ jam();
+ scanptr.p->m_scan_block_no = DBSPJ;
+ }
+
scanptr.p->scanRequestInfo = tmp;
scanptr.p->scanStoredProcId = scanTabReq->storedProcId;
scanptr.p->scanState = ScanRecord::RUNNING;
scanptr.p->m_queued_count = 0;
scanptr.p->m_scan_cookie = RNIL;
scanptr.p->m_close_scan_req = false;
+ scanptr.p->m_pass_all_confs = ScanTabReq::getPassAllConfsFlag(ri);
+ scanptr.p->m_4word_conf = ScanTabReq::get4WordConf(ri);
ScanFragList list(c_scan_frag_pool,
scanptr.p->m_running_scan_frags);
@@ -10744,7 +10781,14 @@ void Dbtc::execDIH_SCAN_GET_NODES_CONF(S
* the KeyInfo and AttrInfo sections when sending.
*/
Uint32 instanceKey = conf->instanceKey;
- scanFragptr.p->lqhBlockref = numberToRef(DBLQH, instanceKey, tnodeid);
+ scanFragptr.p->lqhBlockref = numberToRef(scanptr.p->m_scan_block_no,
+ instanceKey, tnodeid);
+ if (scanptr.p->m_scan_block_no == DBSPJ)
+ {
+ // only 1 instance
+ scanFragptr.p->lqhBlockref = numberToRef(scanptr.p->m_scan_block_no,
+ tnodeid);
+ }
scanFragptr.p->m_connectCount = getNodeInfo(tnodeid).m_connectCount;
/* Determine whether this is the last scanFragReq
@@ -10948,9 +10992,15 @@ void Dbtc::execSCAN_FRAGCONF(Signal* sig
}
if(noCompletedOps == 0 && status != 0 &&
+ !scanptr.p->m_pass_all_confs &&
scanptr.p->scanNextFragId+scanptr.p->m_booked_fragments_count < scanptr.p->scanNoFrag){
/**
- * Start on next fragment
+ * Start on next fragment. Don't do this if we scan via the SPJ block. In
+ * that case, dropping the last SCAN_TABCONF message for a fragment would
+ * mean dropping the 'nodeMask' (which is sent in ScanFragConf::total_len).
+ * This would confuse the API with respect to which pushed operations that
+ * would get new tuples in the next batch. If we use SPJ, we must thus
+ * send SCAN_TABCONF and let the API ask for the next batch.
*/
scanFragptr.p->scanFragState = ScanFragRec::WAIT_GET_PRIMCONF;
scanFragptr.p->startFragTimer(ctcTimer);
@@ -10984,6 +11034,22 @@ void Dbtc::execSCAN_FRAGCONF(Signal* sig
scanptr.p->m_queued_count++;
}
+ if (status != 0 &&
+ scanptr.p->m_pass_all_confs &&
+ scanptr.p->scanNextFragId+scanptr.p->m_booked_fragments_count
+ < scanptr.p->scanNoFrag){
+ /**
+ * nodeMask(=total_len) should be zero since there will be no more
+ * rows from this fragment.
+ */
+ ndbrequire(total_len==0);
+ /**
+ * Now set it to one to tell the API that there may be more rows from
+ * the next fragment.
+ */
+ total_len = 1;
+ }
+
scanFragptr.p->m_scan_frag_conf_status = status;
scanFragptr.p->m_ops = noCompletedOps;
scanFragptr.p->m_totalLen = total_len;
@@ -11542,7 +11608,17 @@ void Dbtc::sendScanTabConf(Signal* signa
jam();
Uint32* ops = signal->getDataPtrSend()+4;
Uint32 op_count = scanPtr.p->m_queued_count;
- if(4 + 3 * op_count > 25){
+
+ Uint32 words_per_op = 4;
+ const Uint32 ref = apiConnectptr.p->ndbapiBlockref;
+ if (!scanPtr.p->m_4word_conf)
+ {
+ jam();
+ words_per_op = 3;
+ }
+
+ if (4 + words_per_op * op_count > 25)
+ {
jam();
ops += 21;
}
@@ -11570,8 +11646,16 @@ void Dbtc::sendScanTabConf(Signal* signa
* ops++ = curr.p->m_apiPtr;
* ops++ = done ? RNIL : curr.i;
- * ops++ = (curr.p->m_totalLen << 10) + curr.p->m_ops;
-
+ if (words_per_op == 4)
+ {
+ * ops++ = curr.p->m_ops;
+ * ops++ = curr.p->m_totalLen;
+ }
+ else
+ {
+ * ops++ = (curr.p->m_totalLen << 10) + curr.p->m_ops;
+ }
+
queued.remove(curr);
if(!done){
delivered.add(curr);
@@ -11606,17 +11690,20 @@ void Dbtc::sendScanTabConf(Signal* signa
}
}
- if(4 + 3 * op_count > 25){
+ if (4 + words_per_op * op_count > 25)
+ {
jam();
LinearSectionPtr ptr[3];
ptr[0].p = signal->getDataPtrSend()+25;
- ptr[0].sz = 3 * op_count;
- sendSignal(apiConnectptr.p->ndbapiBlockref, GSN_SCAN_TABCONF, signal,
- ScanTabConf::SignalLength, JBB, ptr, 1);
- } else {
+ ptr[0].sz = words_per_op * op_count;
+ sendSignal(ref, GSN_SCAN_TABCONF, signal,
+ ScanTabConf::SignalLength, JBB, ptr, 1);
+ }
+ else
+ {
jam();
- sendSignal(apiConnectptr.p->ndbapiBlockref, GSN_SCAN_TABCONF, signal,
- ScanTabConf::SignalLength + 3 * op_count, JBB);
+ sendSignal(ref, GSN_SCAN_TABCONF, signal,
+ ScanTabConf::SignalLength + words_per_op * op_count, JBB);
}
scanPtr.p->m_queued_count = 0;
=== modified file 'storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp'
--- a/storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp 2011-02-03 14:20:36 +0000
+++ b/storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp 2011-02-23 19:28:26 +0000
@@ -100,6 +100,7 @@ static BlockInfo ALL_BLOCKS[] = {
,{ PGMAN_REF, 1 , 0, 0 }
,{ RESTORE_REF,1 , 0, 0 }
,{ DBINFO_REF,1 , 0, 0 }
+ ,{ DBSPJ_REF,1 , 0, 0 }
};
static const Uint32 ALL_BLOCKS_SZ = sizeof(ALL_BLOCKS)/sizeof(BlockInfo);
@@ -124,7 +125,8 @@ static BlockReference readConfigOrder[AL
TSMAN_REF,
LGMAN_REF,
PGMAN_REF,
- RESTORE_REF
+ RESTORE_REF,
+ DBSPJ_REF
};
/*******************************/
@@ -1981,6 +1983,9 @@ void Ndbcntr::execNODE_FAILREP(Signal* s
sendSignal(LGMAN_REF, GSN_NODE_FAILREP, signal,
NodeFailRep::SignalLength, JBB);
+ sendSignal(DBSPJ_REF, GSN_NODE_FAILREP, signal,
+ NodeFailRep::SignalLength, JBB);
+
if (c_stopRec.stopReq.senderRef)
{
jam();
=== modified file 'storage/ndb/src/kernel/blocks/qmgr/Qmgr.hpp'
--- a/storage/ndb/src/kernel/blocks/qmgr/Qmgr.hpp 2011-02-01 23:27:25 +0000
+++ b/storage/ndb/src/kernel/blocks/qmgr/Qmgr.hpp 2011-02-23 19:28:26 +0000
@@ -93,6 +93,7 @@ public:
WAITING_FOR_FAILCONF1 = 3,
WAITING_FOR_FAILCONF2 = 4,
WAITING_FOR_FAILCONF3 = 5,
+ WAITING_FOR_FAILCONF4 = 7,
WAITING_FOR_NDB_FAILCONF = 6
};
=== modified file 'storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp'
--- a/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp 2011-02-01 23:27:25 +0000
+++ b/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp 2011-02-23 19:28:26 +0000
@@ -2862,6 +2862,11 @@ void Qmgr::sendApiFailReq(Signal* signal
sendSignalNoRelease(CMVMI_REF, GSN_ROUTE_ORD, signal,
RouteOrd::SignalLength,
JBA, &handle);
+
+ routeOrd->dstRef = DBSPJ_REF;
+ sendSignalNoRelease(CMVMI_REF, GSN_ROUTE_ORD, signal,
+ RouteOrd::SignalLength,
+ JBA, &handle);
}
/* Suma always notified */
@@ -2906,6 +2911,11 @@ void Qmgr::execAPI_FAILCONF(Signal* sign
else if (failedNodePtr.p->failState == WAITING_FOR_FAILCONF3)
{
jam();
+ failedNodePtr.p->failState = WAITING_FOR_FAILCONF4;
+ }
+ else if (failedNodePtr.p->failState == WAITING_FOR_FAILCONF4)
+ {
+ jam();
failedNodePtr.p->failState = NORMAL;
}
else
@@ -3831,7 +3841,7 @@ void Qmgr::handleApiCloseComConf(Signal*
*/
jam();
sendApiFailReq(signal, nodeId, true); // sumaOnly
- failedNodePtr.p->failState = WAITING_FOR_FAILCONF3;
+ failedNodePtr.p->failState = WAITING_FOR_FAILCONF4;
}
if (getNodeInfo(failedNodePtr.i).getType() == NodeInfo::MGM)
=== modified file 'storage/ndb/src/kernel/vm/mt.cpp'
--- a/storage/ndb/src/kernel/vm/mt.cpp 2011-02-02 00:40:07 +0000
+++ b/storage/ndb/src/kernel/vm/mt.cpp 2011-02-23 19:28:26 +0000
@@ -2659,6 +2659,7 @@ add_main_thr_map()
add_thr_map(PGMAN, 0, thr_LOCAL);
add_thr_map(RESTORE, 0, thr_LOCAL);
add_thr_map(DBINFO, 0, thr_LOCAL);
+ add_thr_map(DBSPJ, 0, thr_GLOBAL);
}
/* Workers added by LocalProxy (before first signal). */
No bundle (reason: useless for push emails).
| Thread |
|---|
| • bzr push into mysql-5.1-telco-7.0 branch (jonas:4226 to 4227) | jonas oreland | 23 Feb |